home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD2.mdf / c / library / dos / grafik / fgl105 / user2.txt < prev   
Encoding:
Text File  |  1991-07-04  |  304.2 KB  |  7,129 lines

  1.  
  2.  
  3. Chapter 8
  4.  
  5. Video Page Management
  6. 110   Fastgraph User's Guide
  7.  
  8.  
  9. Overview
  10.  
  11.      The amount of memory required to store one full screen of information is
  12. called a video page.  This chapter will discuss video pages in detail, along
  13. with the Fastgraph routines you can use to manage video pages.
  14.  
  15.  
  16. Physical Pages and Virtual Pages
  17.  
  18.      Pages that use the memory that resides on the video adapter are called
  19. physical pages or true pages.  The number of physical pages available depends
  20. on the video mode and the amount of memory resident on the user's video
  21. adapter.  All video modes have at least one physical page.
  22.  
  23.      In certain video modes, Fastgraph can allocate available random-access
  24. memory (RAM) and treat this memory as a video page.  Pages that use standard
  25. RAM in this sense are called virtual pages.  From a programmer's perspective,
  26. virtual pages are essentially identical to physical pages.
  27.  
  28.      The following table shows the number of physical pages in each video
  29. mode.  It also indicates whether or not specific video modes support virtual
  30. pages.  A discussion on how to calculate the listed video page sizes (that
  31. is, their memory requirements) was presented in chapter 2.
  32.  
  33.  
  34.         Mode                            Page Size Physical Virtual
  35.        Number Description                in Bytes    Pages   Pages
  36.  
  37.          0    40 column color text          2,000      8      no
  38.          1    40 column color text          2,000      8      no
  39.          2    80 column color text          4,000      4      no
  40.          3    80 column color text          4,000      4      no
  41.          4    320 x 200 CGA graphics       16,000      1      yes
  42.          5    320 x 200 CGA graphics       16,000      1      yes
  43.          6    640 x 200 CGA graphics       16,000      1      yes
  44.          7    80 column monochrome text     4,000      1      yes
  45.          9    320 x 200 Tandy graphics     32,000      1      yes
  46.          11   720 x 348 Hercules graphics  31,320      2      yes
  47.          12   320 x 200 Hercules graphics  31,320      2      yes
  48.          13   320 x 200 EGA graphics       32,000      8      no
  49.          14   640 x 200 EGA graphics       64,000      4      no
  50.          15   640 x 350 EGA mono graphics  56,000      2      no
  51.          16   640 x 350 EGA graphics      112,000      2      no
  52.          17   640 x 480 MCGA/VGA graphics  38,400      1      no
  53.          18   640 x 480 VGA graphics      153,600      1      no
  54.          19   320 x 200 MCGA graphics      64,000      1      yes
  55.          20   320 x 200 VGA graphics       64,000      4      no
  56.          21   320 x 400 VGA graphics      128,000      2      no
  57.  
  58.  
  59. The preceding table assumes the video adapter for EGA and VGA modes contains
  60. 256K bytes of video memory.  For EGA adapters with less video memory, the
  61. number of physical pages is reduced proportionately.  In other words, a 64K
  62. EGA has two video pages available instead of eight pages in mode 13.
  63.                                        Chapter 8:  Video Page Management   111
  64.  
  65.      Physical pages are numbered starting at zero.  For example, there are
  66. four physical video pages available in mode 3, and they are numbered 0 to 3.
  67. Virtual pages are numbered n to 15, where n is the number of physical pages
  68. in that mode.  For example, there are two physical pages (numbered 0 and 1)
  69. and fourteen virtual pages (numbered 2 to 15) in mode 11.  Note only modes 4
  70. through 12 and mode 19 offer virtual pages, and the amount of available RAM
  71. in the user's system may limit the number of virtual pages Fastgraph may use
  72. (this is especially true in mode 19 because of the large page size).
  73.  
  74.  
  75. Pages With Special Meanings
  76.  
  77.      There are three video pages that have special meanings to Fastgraph.
  78. The visual page, as one might guess, is the video page currently visible on
  79. the user's display.  The active page is the video page to which Fastgraph
  80. writes text or graphics information.  The hidden page is meaningful only to a
  81. few Fastgraph routines and will be discussed specifically within the context
  82. of those routines.  The fg_setmode routine sets all three of these pages to
  83. page 0, and it does not matter if these pages are physical or virtual.
  84.  
  85.      One of the most useful features of multiple video pages (either physical
  86. or virtual) is the ability to build a text or graphics image off screen (that
  87. is, on some video page besides the visual page).  Then, once the image is
  88. ready, we can either transfer it to the visual page, or make the page on
  89. which the image resides the visual page.  This feature is especially useful
  90. in animation, for it displays an image instantaneously instead of visibly
  91. updating the screen while producing the image.
  92.  
  93.  
  94. Some Simple Examples
  95.  
  96.      In this section, we will present six variations of a simple program that
  97. uses four video pages.  The program fills each video page with a rectangle
  98. and then displays text containing the video page number in the center of each
  99. page.  The first two examples run in a specific text or graphics video mode
  100. and only use physical pages.  The next two examples also run in a specific
  101. text or graphics video mode, but they also use virtual pages.  The final two
  102. examples are more general and run in several video modes.  You could of
  103. course write a program that essentially does the same thing as the examples
  104. in this section without using multiple video pages.  However, to use
  105. Fastgraph's image display and animation routines effectively, you must first
  106. understand the concept of video pages.
  107.  
  108.      Before proceeding, we must introduce the Fastgraph routines fg_setpage
  109. and fg_setvpage.  The fg_setpage routine defines the active video page, which
  110. causes Fastgraph to put subsequent text and graphics output on that page.
  111. The fg_setvpage routine defines the visual video page displayed on the
  112. screen.  Both routines take a single integer argument between 0 and 15 that
  113. specifies the video page number.  It does not matter if the referenced video
  114. page is a physical page or a virtual page.  As mentioned earlier, fg_setmode
  115. makes page 0 the active and visual video page.
  116.  
  117.      Example 8-1 uses four video pages (numbered 0 to 3) in the 40-column
  118. color text mode (mode 1).  The program first calls fg_testmode to check the
  119. availability of the requested video mode when used with four video pages.  If
  120. it is available, the program calls fg_setmode to establish that video mode.
  121. The first for loop fills each of the four pages with different color
  122. 112   Fastgraph User's Guide
  123.  
  124. rectangles and then displays black text containing the video page number in
  125. the center of each page.  It does this by calling fg_setpage to define the
  126. active video page, fg_setcolor and fg_rect to draw the colored rectangles,
  127. and finally fg_setattr, fg_locate, and fg_text to display the text.  The
  128. program must call fg_locate inside the loop because each video page has its
  129. own text cursor position.  The second for loop successively makes each video
  130. page the visual page; the page remains displayed until you press a key.
  131. After displaying all four video pages, the program restores the original
  132. video mode and screen attributes before returning to DOS.
  133.  
  134.                                  Example 8-1.
  135.  
  136.                #define PAGES 4
  137.  
  138.                main()
  139.                {
  140.                   int color;
  141.                   int old_mode;
  142.                   int page;
  143.                   char string[8];
  144.  
  145.                   if (fg_testmode(1,PAGES) == 0) {
  146.                      printf("This program requires color.\n");
  147.                      exit();
  148.                      }
  149.  
  150.                   old_mode = fg_getmode();
  151.                   fg_setmode(1);
  152.  
  153.                   for (page = 0; page < PAGES; page++) {
  154.                      fg_setpage(page);
  155.                      color = page + 1;
  156.                      fg_setcolor(color);
  157.                      fg_rect(0,fg_getmaxx(),0,fg_getmaxy());
  158.                      fg_setattr(0,color,0);
  159.                      fg_locate(12,17);
  160.                      sprintf(string,"page %d",page);
  161.                      fg_text(string,6);
  162.                      }
  163.  
  164.                   for (page = 0; page < PAGES; page++) {
  165.                      fg_setvpage(page);
  166.                      fg_waitkey();
  167.                      }
  168.  
  169.                   fg_setmode(old_mode);
  170.                   fg_reset();
  171.                }
  172.  
  173.  
  174.      Example 8-2 is similar to example 8-1, but it uses the 320 by 200 EGA
  175. graphics mode (mode 13) instead of a text mode.  Note the only real
  176. difference between this program and the text mode version is the use of
  177. fg_setcolor instead of fg_setattr to make the text appear in black.
  178.  
  179.                                        Chapter 8:  Video Page Management   113
  180.                                  Example 8-2.
  181.  
  182.                #define PAGES 4
  183.  
  184.                main()
  185.                {
  186.                   int color;
  187.                   int old_mode;
  188.                   int page;
  189.                   char string[8];
  190.  
  191.                   if (fg_testmode(13,PAGES) == 0) {
  192.                      printf("This program requires a ");
  193.                      printf("320 x 200 EGA graphics mode.\n");
  194.                      exit();
  195.                      }
  196.  
  197.                   old_mode = fg_getmode();
  198.                   fg_setmode(13);
  199.  
  200.                   for (page = 0; page < PAGES; page++) {
  201.                      fg_setpage(page);
  202.                      color = page + 1;
  203.                      fg_setcolor(color);
  204.                      fg_rect(0,fg_getmaxx(),0,fg_getmaxy());
  205.                      fg_setcolor(0);
  206.                      fg_locate(12,17);
  207.                      sprintf(string,"page %d",page);
  208.                      fg_text(string,6);
  209.                      }
  210.  
  211.                   for (page = 0; page < PAGES; page++) {
  212.                      fg_setvpage(page);
  213.                      fg_waitkey();
  214.                      }
  215.  
  216.                   fg_setmode(old_mode);
  217.                   fg_reset();
  218.                }
  219.  
  220.      Virtual video pages are created with Fastgraph's fg_allocate routine.
  221. The fg_allocate routine reserves random-access memory (RAM) which Fastgraph
  222. then treats as a video page.  The amount of memory required depends on the
  223. current video mode.  The fg_allocate routine takes a single integer argument
  224. that specifies the page number by which the virtual page will be referenced.
  225. This value must be between 0 and 15.
  226.  
  227.      If you attempt to create a virtual page with a page number already
  228. assigned to a physical page, fg_allocate does nothing.  For example, in the
  229. Hercules graphics modes (modes 11 and 12) there are two physical pages
  230. numbered 0 and 1.  Virtual pages in the Hercules graphics modes must thus
  231. have page numbers between 2 and 15.  If you tell fg_allocate to create a
  232. Hercules virtual page numbered 0 or 1, it does nothing because the requested
  233. video page already exists as a physical page.  Similarly, if you use the
  234. fg_allocate routine in a video mode that does not support virtual video
  235. pages, it simply returns without doing anything.
  236. 114   Fastgraph User's Guide
  237.  
  238.      A possible problem with fg_allocate can occur when there is not enough
  239. memory available for creating a virtual page in the current video mode.  The
  240. fg_allocate routine returns as its function value a status code indicating
  241. whether or not it was successful.  The possible values of the status code
  242. are:
  243.  
  244.  
  245.    value   meaning
  246.  
  247.      0     virtual page created
  248.      7     virtual page created, but memory control blocks were destroyed
  249.      8     insufficient memory to create the virtual page
  250.  
  251.  
  252. If you use the fg_testmode or fg_bestmode routines to check if the required
  253. number of video pages are available when using the requested video mode, you
  254. should not need to monitor the status code returned by the fg_allocate
  255. routine.
  256.  
  257.      The fg_freepage routine releases the memory for a virtual page created
  258. with the fg_allocate routine.  It requires a single integer argument that
  259. specifies the virtual page number to release.  This value must be between 0
  260. and 15.  If you try to release a physical video page, or release a virtual
  261. page that was never created, fg_freepage does nothing.  It is a good idea to
  262. use fg_freepage to release all virtual video pages before a program returns
  263. control to DOS, or just before a program selects a new video mode.
  264.  
  265.      Example 8-3 is also similar to example 8-1, but it uses the monochrome
  266. text mode (mode 7).  Because the monochrome text mode only has one physical
  267. video page, we must use virtual video pages for page numbers 1, 2, and 3.
  268. Note how the fg_allocate and fg_freepage routines are used to create and
  269. release the virtual video pages in this example.
  270.  
  271.                                  Example 8-3.
  272.  
  273.              #define PAGES 4
  274.  
  275.              main()
  276.              {
  277.                 int old_mode;
  278.                 int page;
  279.                 char string[8];
  280.  
  281.                 if (fg_testmode(7,PAGES) == 0) {
  282.                    printf("This program requires monochrome.\n");
  283.                    exit();
  284.                    }
  285.  
  286.                 old_mode = fg_getmode();
  287.                 fg_setmode(7);
  288.                 fg_cursor(0);
  289.  
  290.                 for (page = 0; page < PAGES; page++) {
  291.                    fg_allocate(page);
  292.                    fg_setpage(page);
  293.                    fg_setcolor(7);
  294.  
  295.                                        Chapter 8:  Video Page Management   115
  296.  
  297.                    fg_rect(0,fg_getmaxx(),0,fg_getmaxy());
  298.                    fg_setattr(0,7,0);
  299.                    fg_locate(12,37);
  300.                    sprintf(string,"page %d",page);
  301.                    fg_text(string,6);
  302.                    }
  303.  
  304.                 for (page = 0; page < PAGES; page++) {
  305.                    fg_setvpage(page);
  306.                    fg_waitkey();
  307.                    fg_freepage(page);
  308.                    }
  309.  
  310.                 fg_setmode(old_mode);
  311.                 fg_reset();
  312.              }
  313.  
  314.      Example 8-4 is similar to example 8-3, but it uses the standard Hercules
  315. graphics mode (mode 11) instead of the monochrome text mode.  Because the
  316. Hercules graphics modes have two physical video pages, we must use virtual
  317. video pages for page numbers 2 and 3.  Note the only real difference between
  318. this program and the text mode version is the use of fg_setcolor instead of
  319. fg_setattr to make the text appear in black.
  320.  
  321.                                  Example 8-4.
  322.  
  323.                #define PAGES 4
  324.  
  325.                main()
  326.                {
  327.                   int old_mode;
  328.                   int page;
  329.                   char string[8];
  330.  
  331.                   if (fg_testmode(11,PAGES) == 0) {
  332.                      printf("This program requires Hercules ");
  333.                      printf("monochrome graphics.\n");
  334.                      exit();
  335.                      }
  336.  
  337.                   old_mode = fg_getmode();
  338.                   fg_setmode(11);
  339.  
  340.                   for (page = 0; page < PAGES; page++) {
  341.                      fg_allocate(page);
  342.                      fg_setpage(page);
  343.                      fg_setcolor(7);
  344.                      fg_rect(0,fg_getmaxx(),0,fg_getmaxy());
  345.                      fg_setcolor(0);
  346.                      fg_locate(12,37);
  347.                      sprintf(string,"page %d",page);
  348.                      fg_text(string,6);
  349.                      }
  350.  
  351.                   for (page = 0; page < PAGES; page++) {
  352.                      fg_setvpage(page);
  353.  
  354. 116   Fastgraph User's Guide
  355.                      fg_waitkey();
  356.                      fg_freepage(page);
  357.                      }
  358.  
  359.                   fg_setmode(old_mode);
  360.                   fg_reset();
  361.                }
  362.  
  363.      Example 8-5 is a generalized version of examples 8-1 and 8-3 that runs
  364. in any 80-column text video mode.  To simplify the program, each video page
  365. is filled with rectangles of the same color.  Note that fg_allocate and
  366. fg_freepage are used to manage the virtual video pages in case fg_bestmode
  367. selects the monochrome text mode (mode 7).  If fg_bestmode selects one of the
  368. 80-column color text modes (which have four physical video pages),
  369. fg_allocate and fg_freepage will simply return without doing anything.
  370.  
  371.                                  Example 8-5.
  372.  
  373.                 #define PAGES 4
  374.  
  375.                 main()
  376.                 {
  377.                    int old_mode, new_mode;
  378.                    int page;
  379.                    char string[8];
  380.  
  381.                    new_mode = fg_bestmode(80,25,PAGES);
  382.                    if (new_mode < 0) {
  383.                       printf("This program requires ");
  384.                       printf("an 80-column display.\n");
  385.                       exit();
  386.                       }
  387.  
  388.                    old_mode = fg_getmode();
  389.                    fg_setmode(new_mode);
  390.                    fg_cursor(0);
  391.  
  392.                    for (page = 0; page < PAGES; page++) {
  393.                       fg_allocate(page);
  394.                       fg_setpage(page);
  395.                       fg_setcolor(7);
  396.                       fg_rect(0,fg_getmaxx(),0,fg_getmaxy());
  397.                       fg_setattr(0,7,0);
  398.                       fg_locate(12,37);
  399.                       sprintf(string,"page %d",page);
  400.                       fg_text(string,6);
  401.                       }
  402.  
  403.                    for (page = 0; page < PAGES; page++) {
  404.                       fg_setvpage(page);
  405.                       fg_waitkey();
  406.                       fg_freepage(page);
  407.                       }
  408.  
  409.                    fg_setmode(old_mode);
  410.                    fg_reset();
  411.                 }
  412.  
  413.                                        Chapter 8:  Video Page Management   117
  414.  
  415.  
  416.      Example 8-6 is a generalized version of examples 8-2 and 8-4 that runs
  417. in any 320 by 200 graphics video mode.  To simplify the program, each video
  418. page is filled with rectangles of the same color.  As in example 8-5,
  419. fg_allocate and fg_freepage are used to manage the virtual video pages in
  420. case fg_bestmode selects a video mode with fewer than four physical video
  421. pages.  Note the only real difference between this program and the text mode
  422. version is the use of fg_setcolor instead of fg_setattr to make the text
  423. appear in black.
  424.  
  425.                                  Example 8-6.
  426.  
  427.                 #define PAGES 4
  428.  
  429.                 main()
  430.                 {
  431.                    int old_mode, new_mode;
  432.                    int page;
  433.                    char string[8];
  434.  
  435.                    new_mode = fg_bestmode(320,200,PAGES);
  436.                    if (new_mode < 0) {
  437.                       printf("This program requires a ");
  438.                       printf("320 x 200 graphics mode.\n");
  439.                       exit();
  440.                       }
  441.  
  442.                    old_mode = fg_getmode();
  443.                    fg_setmode(new_mode);
  444.  
  445.                    for (page = 0; page < PAGES; page++) {
  446.                       fg_allocate(page);
  447.                       fg_setpage(page);
  448.                       fg_setcolor(15);
  449.                       fg_rect(0,fg_getmaxx(),0,fg_getmaxy());
  450.                       fg_setcolor(0);
  451.                       fg_locate(12,17);
  452.                       sprintf(string,"page %d",page);
  453.                       fg_text(string,6);
  454.                       }
  455.  
  456.                    for (page = 0; page < PAGES; page++) {
  457.                       fg_setvpage(page);
  458.                       fg_waitkey();
  459.                       fg_freepage(page);
  460.                       }
  461.  
  462.                    fg_setmode(old_mode);
  463.                    fg_reset();
  464.                 }
  465.  
  466. 118   Fastgraph User's Guide
  467.  
  468.  
  469. Text Cursors
  470.  
  471.      As mentioned in the previous chapter, Fastgraph draws hardware
  472. characters at the position defined by the text cursor.  Like the graphics
  473. cursor, the text cursor is not a cursor in the true sense, but is simply a
  474. pair of character space (row,column) coordinates with a special meaning.  The
  475. first 8 video pages (that is, pages 0 through 7) each have their own text
  476. cursor.  The last 8 video pages (pages 8 through 15) respectively share the
  477. same text cursor positions as the first 8 pages.  This means the fg_locate
  478. routine will update one of 8 different text cursors depending on the active
  479. video page.  Similarly, the fg_where routine returns the text cursor position
  480. for the active page.  The fg_setmode routine sets all 8 text cursor positions
  481. to the character space coordinates (0,0).
  482.  
  483.      Example 8-7 demonstrates the use of different text cursors in an 80-
  484. column color text mode (mode 3).  The program first displays the text "Page "
  485. on video page 0 (the visible page) and waits for a keystroke.  It then makes
  486. page 1 the active video page, changes the text cursor location for that page,
  487. and displays the text "Page 1" on video page 1.  Next, it appends the
  488. character "0" to the text originally displayed on page 0.  Note it is not
  489. necessary to restore the text cursor position for page 0 because it is
  490. unaffected by changing the text cursor for page 1.  After waiting for another
  491. keystroke, the program makes video page 1 the visual page and then waits for
  492. yet another keystroke before returning to DOS.
  493.  
  494.                                  Example 8-7.
  495.  
  496.                          main()
  497.                          {
  498.                             int old_mode;
  499.  
  500.                             old_mode = fg_getmode();
  501.                             fg_setmode(3);
  502.                             fg_cursor(0);
  503.                             fg_setattr(10,0,0);
  504.  
  505.                             fg_locate(1,0);
  506.                             fg_text("Page ",5);
  507.                             fg_waitkey();
  508.  
  509.                             fg_setpage(1);
  510.                             fg_locate(23,0);
  511.                             fg_text("Page 1",6);
  512.  
  513.                             fg_setpage(0);
  514.                             fg_text("0",1);
  515.                             fg_waitkey();
  516.  
  517.                             fg_setvpage(1);
  518.                             fg_waitkey();
  519.  
  520.                             fg_setmode(old_mode);
  521.                             fg_reset();
  522.                          }
  523.  
  524.                                        Chapter 8:  Video Page Management   119
  525.  
  526.  
  527. Obtaining Video Page Information
  528.  
  529.      Fastgraph includes two routines, fg_getpage and fg_getvpage, that
  530. respectively return the current active or visual video page number.  Each
  531. routine returns the video page number as its function value, and neither
  532. routine requires any arguments.
  533.  
  534.      The fg_getaddr routine is sometimes useful when using virtual pages.  It
  535. returns as its function value the segment address for the start of the active
  536. video page.  It does not require any arguments.  Although fg_getaddr is more
  537. useful when using virtual video pages, it works equally well when using
  538. physical video pages.
  539.  
  540.      Example 8-8 illustrates the use of the fg_getpage, fg_getvpage, and
  541. fg_getaddr routines in the standard CGA color graphics mode (mode 4).  This
  542. video mode offers only one physical page, so the program uses fg_allocate to
  543. create a virtual video page (page 1).  After creating the virtual page, the
  544. program makes it the active video page; page 0 remains the visual video page.
  545. The fg_getpage routine then returns the active page number (1), followed by a
  546. call to fg_getvpage to return the visual page number (0).  Next, the program
  547. uses fg_getaddr to return the segment address for video pages 0 and 1.
  548. Finally, it restores the original video mode and screen attributes, displays
  549. the returned values, and returns to DOS.
  550.  
  551.                                  Example 8-8.
  552.  
  553.                  main()
  554.                  {
  555.                     int old_mode;
  556.                     int active, visual;
  557.                     int page0, page1;
  558.  
  559.                     old_mode = fg_getmode();
  560.                     fg_setmode(4);
  561.                     fg_allocate(1);
  562.                     fg_setpage(1);
  563.  
  564.                     active = fg_getpage();
  565.                     visual = fg_getvpage();
  566.  
  567.                     fg_setpage(0);
  568.                     page0 = fg_getaddr();
  569.                     fg_setpage(1);
  570.                     page1 = fg_getaddr();
  571.  
  572.                     fg_freepage(1);
  573.                     fg_setmode(old_mode);
  574.                     fg_reset();
  575.  
  576.                     printf("Active page is %d.\n",active);
  577.                     printf("Visual page is %d.\n",visual);
  578.                     printf("Page 0 address is %4X\n",page0);
  579.                     printf("Page 1 address is %4X\n",page1);
  580.                  }
  581.  
  582. 120   Fastgraph User's Guide
  583.  
  584.  
  585. Considerations for Virtual Pages
  586.  
  587.      When you are using virtual pages, you should avoid using the fg_setvpage
  588. routine in sections of the program that require fast screen updates or
  589. animation sequences.  This is because the PC and PS/2 video BIOS are only
  590. capable of displaying physical pages.  To compensate for this restriction,
  591. Fastgraph exchanges the contents of a physical page with the requested
  592. virtual page.  In other words, if page 1 is a virtual page and you make it
  593. the visual page, Fastgraph will exchange the contents of page 1 with whatever
  594. page was previously the visual page.  This does not mean Fastgraph's page
  595. numbers change because Fastgraph also maintains an internal table containing
  596. video page addresses and also exchanges the two corresponding table entries.
  597. As before, you would make page 1 the active video page if you wanted to write
  598. something to the visual page.
  599.  
  600.      About the only other potential problem when using virtual pages is what
  601. happens when you attempt to write to a non-existent video page (for example,
  602. if you write to virtual video page 1 before creating it with fg_allocate).
  603. In this case, Fastgraph simply redirects the video output to the visual page.
  604.  
  605.  
  606. Summary of Video Page Management Routines
  607.  
  608.      This section summarizes the functional descriptions of the Fastgraph
  609. routines presented in this chapter.  More detailed information about these
  610. routines, including their arguments and return values, may be found in the
  611. Fastgraph Reference Manual.
  612.  
  613.      FG_ALLOCATE creates a virtual video page.  The amount of memory required
  614. depends on the current video mode.  This routine has no effect if it
  615. references a physical video page.
  616.  
  617.      FG_FREEPAGE releases a virtual video page created with the fg_allocate
  618. routine.  This routine has no effect if it references a physical video page,
  619. or a virtual page that was never created.
  620.  
  621.      FG_GETADDR returns the segment address of the active video page.
  622.  
  623.      FG_GETPAGE returns the active video page number.
  624.  
  625.      FG_GETVPAGE returns the visual video page number.
  626.  
  627.      FG_SETPAGE establishes the active video page.  It may be a physical or
  628. virtual page.
  629.  
  630.      FG_SETVPAGE establishes the visual video page.  It may be a physical or
  631. virtual page.
  632.  
  633.  
  634. Chapter 9
  635.  
  636. Images and Image Management
  637. 122   Fastgraph User's Guide
  638.  
  639.  
  640. Overview
  641.  
  642.      Within the context of Fastgraph, an image is a rectangular area of video
  643. memory containing some type of picture.  An image might be something as
  644. simple as a pointing hand icon, or as detailed as the dashboard of a sports
  645. car.  Fastgraph includes a number of routines to display, retrieve, and
  646. manipulate images, as well as transfer them between different areas of video
  647. memory.  This chapter will discuss these routines in detail.  The information
  648. presented here, combined with the video page management techniques described
  649. in the previous chapter, will provide the tools we need for sophisticated
  650. animation techniques.
  651.  
  652.  
  653. Mode-Independent Bit-Mapped Images
  654.  
  655.      This section will discuss the image display routines that use the same
  656. bit-mapped image format for all graphics video modes.  Another class of
  657. routines, described in the next section, use different formats for different
  658. video modes.  While these mode-independent image display routines are more
  659. general, they achieve this generality at the sake of some execution speed.
  660. This may especially be a concern if the image is large, or if speed is
  661. critical in an application (as in arcade-style graphics).  For many programs,
  662. however, the mode-independent routines provide all the image display
  663. capability that is needed.
  664.  
  665.      Let's begin with an example of a very simple image.  Suppose we need to
  666. display a small triangle whose perimeter is a different color than its
  667. interior.  To use this image with Fastgraph, we must inscribe it in a
  668. rectangular area.  Hence, the pixel representation of our triangle might
  669. appear as shown below.
  670.  
  671.                               . . . . * . . . .
  672.                               . . . * x * . . .
  673.                               . . * x x x * . .
  674.                               . * x x x x x * .
  675.                               * * * * * * * * *
  676.  
  677.      As shown in this diagram, our triangle is 9 pixels wide at its base and
  678. 5 pixels high.  The pixels indicated by an asterisk (*) are the triangle's
  679. perimeter, while those indicated by an x represent its interior points.  We
  680. need to distinguish between these pixels because they will be different
  681. colors.  The pixels shown as periods (.) are not part of the triangle itself.
  682. They are required to make the image rectangular, so from Fastgraph's
  683. perspective they are indeed part of the image.
  684.  
  685.      The Fastgraph routine fg_drawmap is a suitable routine for drawing our
  686. triangle.  To use fg_drawmap, we must create a separate bit map for each
  687. color in the image (excluding the points used to fill the rectangular region,
  688. which is considered transparent).  In this example, we will thus need two bit
  689. maps -- one for the perimeter points, and one for the interior points.  Let's
  690. break the image into these two bit maps.
  691.                                  Chapter 9:  Images and Image Management   123
  692.  
  693.                   . . . . * . . . .        . . . . . . . . .
  694.                   . . . * . * . . .        . . . . x . . . .
  695.                   . . * . . . * . .        . . . x x x . . .
  696.                   . * . . . . . * .        . . x x x x x . .
  697.                   * * * * * * * * *        . . . . . . . . .
  698.  
  699.                   perimeter points          interior points
  700.  
  701.      The next step is to convert these two bit maps into their binary
  702. representations.  Just as there are eight bits in a byte, we will create a
  703. data structure (an array in this case) with each byte holding eight pixels.
  704. Bits that are set (1) indicate the corresponding pixel will appear displayed
  705. in the color associated with that bit map.  Bits that are reset (0) leave the
  706. corresponding pixel unchanged.  The size of each bit map array must be at
  707. least 10 bytes because each bit map contains five rows with nine pixels in
  708. each row (that is, two bytes are required for each row of the image).  Hence,
  709. when we convert these bit maps to their binary representations, and
  710. subsequently to their hexadecimal equivalent, the results will appear as
  711. shown below.  The bits displayed in boldface represent the actual image; the
  712. other bits are filler bits needed to complete each row of the bit maps after
  713. the ninth pixel.  All filler bits must be zero.
  714.  
  715.  
  716.               0 0 0 0 1 0 0 0   0 0 0 0 0 0 0 0         08   00
  717.  
  718.               0 0 0 1 0 1 0 0   0 0 0 0 0 0 0 0         14   00
  719.  
  720.               0 0 1 0 0 0 1 0   0 0 0 0 0 0 0 0         22   00
  721.  
  722.               0 1 0 0 0 0 0 1   0 0 0 0 0 0 0 0         41   00
  723.  
  724.               1 1 1 1 1 1 1 1   1 0 0 0 0 0 0 0         FF   80
  725.  
  726.                               perimeter bit map
  727.  
  728.  
  729.               0 0 0 0 0 0 0 0   0 0 0 0 0 0 0 0         00   00
  730.  
  731.               0 0 0 0 1 0 0 0   0 0 0 0 0 0 0 0         08   00
  732.  
  733.               0 0 0 1 1 1 0 0   0 0 0 0 0 0 0 0         1C   00
  734.  
  735.               0 0 1 1 1 1 1 0   0 0 0 0 0 0 0 0         3E   00
  736.  
  737.               0 0 0 0 0 0 0 0   0 0 0 0 0 0 0 0         00   00
  738.  
  739.                                interior bit map
  740.  
  741.      The next question is the order in which the bit maps are stored in the
  742. corresponding data structures.  Since our data structure is an array, it is
  743. only necessary to show the relationship of the subscripts to the bit map
  744. structures above.  The next diagram shows the subscript order for the case of
  745. a two-column by five-row bit map.
  746. 124   Fastgraph User's Guide
  747.  
  748.  
  749.                                   [8]   [9]
  750.  
  751.                                   [6]   [7]
  752.  
  753.                                   [4]   [5]
  754.  
  755.                                   [2]   [3]
  756.  
  757.                                   [0]   [1]
  758.  
  759.      From this diagram, we see the first element of the array (that is, the
  760. element with subscript [0]) represents the lower left corner of the image.
  761. The subscript progression then continues right until reaching the end of the
  762. first row.  It then resumes at the leftmost element of the second row and
  763. continues to the right until the end of that row.  It continues in this
  764. manner for all remaining rows.
  765.  
  766.      We are now ready to present an example program to display our triangle.
  767. The program will use the Fastgraph routine fg_drawmap, which expects three
  768. arguments.  The first argument is the bit map array (passed by reference),
  769. the second is the width of the bit map in bytes, and the last is the height
  770. of the bit map in pixel rows.  The fg_drawmap routine displays the image such
  771. that its lower left corner is at the graphics cursor position on the active
  772. video page.  The routine has no effect in text video modes.  Additionally,
  773. fg_drawmap displays the image using the current color index, which means we
  774. will need to call fg_drawmap once for each color in the image.
  775.  
  776.      Example 9-1 runs in any 320 by 200 color graphics mode (it could be made
  777. to run in mode 12 too, but that would detract from the purpose of the
  778. example).  After establishing the video mode, the program uses fg_rect to
  779. fill the entire screen with a gray rectangle (white in CGA).  Next, the
  780. program establishes (156,101) as the graphics cursor position; this causes
  781. the triangle to be centered on the screen.  The two calls to fg_drawmap, one
  782. for each of the colors in the image, actually display the triangle.  Note
  783. especially how fg_setcolor is used before each call to fg_drawmap to define
  784. the current color index.  The end result is a triangle with a blue perimeter
  785. (cyan in CGA) and green interior (magenta in CGA).
  786.  
  787.                                  Example 9-1.
  788.  
  789.              char perimeter[] = {
  790.                 0xFF,0x80,0x41,0x00,0x22,0x00,0x14,0x00,0x08,0x00
  791.                 };
  792.              char interior[] = {
  793.                 0x00,0x00,0x3E,0x00,0x1C,0x00,0x08,0x00,0x00,0x00
  794.                 };
  795.  
  796.              main()
  797.              {
  798.                 int old_mode, new_mode;
  799.  
  800.                 new_mode = fg_bestmode(320,200,1);
  801.                 if (new_mode < 0 || new_mode == 12) {
  802.                    printf("This program requires a 320 ");
  803.                    printf("x 200 color graphics mode.\n");
  804.  
  805.                                  Chapter 9:  Images and Image Management   125
  806.  
  807.                    exit();
  808.                    }
  809.  
  810.                 old_mode = fg_getmode();
  811.                 fg_setmode(new_mode);
  812.  
  813.                 fg_setcolor(7);
  814.                 fg_rect(0,319,0,199);
  815.  
  816.                 fg_move(156,101);
  817.                 fg_setcolor(1);
  818.                 fg_drawmap(perimeter,2,5);
  819.                 fg_setcolor(2);
  820.                 fg_drawmap(interior,2,5);
  821.                 fg_waitkey();
  822.  
  823.                 fg_setmode(old_mode);
  824.                 fg_reset();
  825.              }
  826.  
  827.  
  828.      The different color bit maps used by fg_drawmap do not all have to be
  829. the same size.  In our triangle example, the perimeter is 9 pixels wide by 5
  830. pixels high, but the interior is only 5 pixels wide by 3 pixels high.  Hence,
  831. the bit map for the interior pixels only requires one byte for each of its
  832. the three rows, so we can store it in a three-byte array.  Its structure
  833. would be:
  834.  
  835.                                   [2]   08
  836.  
  837.                                   [1]   1C
  838.  
  839.                                   [0]   3E
  840.  
  841.      Example 9-2 is similar to example 9-1, but it uses a three-byte array
  842. for the interior bit map.  Note the second call to fg_move in this example.
  843. It is needed because the bottom row of the smaller interior bit map
  844. corresponds to the second row of the larger perimeter bit map.  In other
  845. words, the interior bit map must be displayed one row above the perimeter bit
  846. map.
  847.  
  848.                                  Example 9-2.
  849.  
  850.              char perimeter[] = {
  851.                 0xFF,0x80,0x41,0x00,0x22,0x00,0x14,0x00,0x08,0x00
  852.                 };
  853.              char interior[] = {
  854.                 0x3E,0x1C,0x08
  855.                 };
  856.  
  857.              main()
  858.              {
  859.                 int old_mode, new_mode;
  860.  
  861.                 new_mode = fg_bestmode(320,200,1);
  862.  
  863. 126   Fastgraph User's Guide
  864.  
  865.                 if (new_mode < 0 || new_mode == 12) {
  866.                    printf("This program requires a 320 ");
  867.                    printf("x 200 color graphics mode.\n");
  868.                    exit();
  869.                    }
  870.  
  871.                 old_mode = fg_getmode();
  872.                 fg_setmode(new_mode);
  873.  
  874.                 fg_setcolor(7);
  875.                 fg_rect(0,319,0,199);
  876.  
  877.                 fg_move(156,101);
  878.                 fg_setcolor(1);
  879.                 fg_drawmap(perimeter,2,5);
  880.                 fg_move(156,100);
  881.                 fg_setcolor(2);
  882.                 fg_drawmap(interior,1,3);
  883.                 fg_waitkey();
  884.  
  885.                 fg_setmode(old_mode);
  886.                 fg_reset();
  887.              }
  888.  
  889.  
  890.      In example 9-2, the time required to execute the second call to fg_move
  891. may not be worth the saving of 7 bytes.  When array space is critical, or
  892. when the images are larger, the use of smaller bit maps for certain colors
  893. may be more valuable.
  894.  
  895.      Yet another possibility for example 9-2 would be to shift the elements
  896. of the interior bit map two pixels to the left.  In this way, the bit map
  897. would be aligned against the left side of the array, just as the perimeter
  898. bit map is.  The three values comprising the interior bit map would then
  899. become F8, 70, and 20.  We would also need to change the x coordinate in the
  900. second call to fg_move from 156 to 158.
  901.  
  902.  
  903. Mode-Specific Bit-Mapped Images
  904.  
  905.      This section will discuss the image display routines that use bit-mapped
  906. image formats that are specific to each text and graphics video mode.  The
  907. different image formats closely resemble the structure of video memory in
  908. each mode, so these routines are much faster than displaying mode-independent
  909. bit maps with fg_drawmap.  If you use the mode-specific bit maps in a program
  910. that supports several video modes, there will be some additional programming
  911. that is not needed when using mode-independent bit maps.  In most cases,
  912. however, your efforts will be rewarded with considerably faster graphics.
  913.  
  914.      We'll demonstrate the use of mode-specific bit maps in graphics modes
  915. with the familiar two-color triangle whose pixel representation appears
  916. below.
  917.                                  Chapter 9:  Images and Image Management   127
  918.  
  919.                               . . . . * . . . .
  920.                               . . . * x * . . .
  921.                               . . * x x x * . .
  922.                               . * x x x x x * .
  923.                               * * * * * * * * *
  924.  
  925.      As before, our triangle is 9 pixels wide at its base and 5 pixels high.
  926. The pixels indicated by an asterisk (*) are the triangle's perimeter, while
  927. those indicated by an x represent its interior points.  We need to
  928. distinguish between these pixels because they will be different colors.  The
  929. pixels shown as periods (.) are not part of the triangle itself.  They are
  930. required to make the image rectangular, so from Fastgraph's perspective they
  931. are indeed part of the image.
  932.  
  933.  
  934. Regular Images
  935.  
  936.      The Fastgraph routine fg_drwimage displays regular mode-specific bit-
  937. mapped images (by regular, we mean an image that is neither clipped nor
  938. rotated).  Its arguments are identical to the fg_drawmap routine, and the bit
  939. map array's subscript order is also the same as for fg_drawmap.  The major
  940. difference is the bit map structure -- only in the EGA and VGA video modes
  941. (modes 13 through 18) do we create separate bit maps for each color.  In all
  942. other video modes, we combine the information for all colors into a single
  943. bit map.  This is consistent with the structure and accessibility of video
  944. memory for the various modes.  As in all of the other image display routines,
  945. fg_drwimage displays the image on the active video page with its lower left
  946. corner at the graphics cursor position (or the text cursor position for text
  947. modes).  We'll now examine the use of fg_drwimage in several video modes.
  948.  
  949. CGA four-color graphics modes
  950.  
  951.      In the four-color CGA graphics modes (modes 4 and 5), each pixel can
  952. assume a value between 0 and 3.  This means it takes two bits to represent a
  953. pixel, or put another way, each byte of video memory holds four pixels.  Our
  954. triangle image is nine pixels wide, so three bytes are needed for each row of
  955. the image.  Because the image is five pixels high, we need a bit map array of
  956. at least 15 bytes (five rows times three bytes per row) to hold the image.
  957.  
  958.      The image's binary representation and its hexadecimal equivalent for the
  959. four-color CGA graphics modes are shown below.  The binary values displayed
  960. in boldface represent the actual image; the other bits are the filler bits
  961. needed to complete each row of the bit map after the ninth pixel.  We have
  962. coded the perimeter pixels to be color 1 (01 binary) and the interior pixels
  963. to be color 2 (10 binary).  Any pixel whose value is zero (00 binary) is
  964. transparent and will thus leave the contents of video memory at that position
  965. unchanged.
  966. 128   Fastgraph User's Guide
  967.  
  968.  
  969.          00 00 00 00   01 00 00 00   00 00 00 00         00   40   00
  970.  
  971.          00 00 00 01   10 01 00 00   00 00 00 00         01   90   00
  972.  
  973.          00 00 01 10   10 10 01 00   00 00 00 00         06   A4   00
  974.  
  975.          00 01 10 10   10 10 10 01   00 00 00 00         1A   A9   00
  976.  
  977.          01 01 01 01   01 01 01 01   01 00 00 00         55   55   40
  978.  
  979.  
  980.      Example 9-3 uses this mode-specific bit map to display the triangle in
  981. the standard CGA four-color graphics mode (mode 4).  After establishing the
  982. video mode, the program uses fg_rect to fill the entire screen with a white
  983. rectangle.  Next, the program establishes (156,101) as the graphics cursor
  984. position; this causes the triangle to be centered on the screen.  The call to
  985. fg_drwimage produces a triangle with a cyan perimeter (color 1) and a magenta
  986. interior (color 2).
  987.  
  988.                                  Example 9-3.
  989.  
  990.               char triangle[] = {
  991.                  0x55,0x55,0x40, 0x1A,0xA9,0x00, 0x06,0xA4,0x00,
  992.                  0x01,0x90,0x00, 0x00,0x40,0x00
  993.                  };
  994.  
  995.               main()
  996.               {
  997.                  int old_mode;
  998.  
  999.                  if (fg_testmode(4,1) == 0) {
  1000.                     printf("This program requires a 320 ");
  1001.                     printf("x 200 CGA graphics mode.\n");
  1002.                     exit();
  1003.                     }
  1004.  
  1005.                  old_mode = fg_getmode();
  1006.                  fg_setmode(4);
  1007.  
  1008.                  fg_setcolor(7);
  1009.                  fg_rect(0,319,0,199);
  1010.  
  1011.                  fg_move(156,101);
  1012.                  fg_drwimage(triangle,3,5);
  1013.                  fg_waitkey();
  1014.  
  1015.                  fg_setmode(old_mode);
  1016.                  fg_reset();
  1017.               }
  1018.  
  1019.  
  1020. CGA two-color graphics mode
  1021.  
  1022.      In the two-color CGA graphics mode (mode 6), each pixel can assume the
  1023. values 0 or 1.  This means it takes just one bit to represent a pixel, so
  1024. each byte of video memory holds eight pixels.  Our triangle image is nine
  1025.                                  Chapter 9:  Images and Image Management   129
  1026.  
  1027. pixels wide, so two bytes are needed for each row of the image.  Because the
  1028. image is five pixels high, we need a bit map array of at least 10 bytes (five
  1029. rows times two bytes per row) to hold the image.
  1030.  
  1031.      The image's binary representation and its hexadecimal equivalent for the
  1032. two-color CGA graphics mode is shown below.  The binary values displayed in
  1033. boldface represent the actual image; the other bits are the filler bits
  1034. needed to complete each row of the bit map after the ninth pixel.  We have
  1035. coded both the perimeter pixels and the interior pixels to be color 1.  Any
  1036. pixel whose value is zero is transparent and will thus leave the contents of
  1037. video memory at that position unchanged.
  1038.  
  1039.               0 0 0 0 1 0 0 0   0 0 0 0 0 0 0 0         08   00
  1040.  
  1041.               0 0 0 1 1 1 0 0   0 0 0 0 0 0 0 0         1C   00
  1042.  
  1043.               0 0 1 1 1 1 1 0   0 0 0 0 0 0 0 0         3E   00
  1044.  
  1045.               0 1 1 1 1 1 1 1   0 0 0 0 0 0 0 0         7F   00
  1046.  
  1047.               1 1 1 1 1 1 1 1   1 0 0 0 0 0 0 0         FF   80
  1048.  
  1049.      Example 9-4 uses this mode-specific bit map to display the triangle in
  1050. the CGA two-color graphics mode (mode 6).  After establishing the video mode,
  1051. the program establishes (316,101) as the graphics cursor position; this
  1052. causes the triangle to be centered on the screen.  The call to fg_drwimage
  1053. produces a solid triangle.
  1054.  
  1055.                                  Example 9-4.
  1056.  
  1057.                   char triangle[] = {
  1058.                      0xFF,0x80, 0x7F,0x00, 0x3E,0x00,
  1059.                      0x1C,0x00, 0x08,0x00
  1060.                      };
  1061.  
  1062.                   main()
  1063.                   {
  1064.                      int old_mode;
  1065.  
  1066.                      if (fg_testmode(6,1) == 0) {
  1067.                         printf("This program requires a ");
  1068.                         printf("CGA graphics mode.\n");
  1069.                         exit();
  1070.                         }
  1071.  
  1072.                      old_mode = fg_getmode();
  1073.                      fg_setmode(6);
  1074.  
  1075.                      fg_move(316,101);
  1076.                      fg_drwimage(triangle,2,5);
  1077.                      fg_waitkey();
  1078.  
  1079.                      fg_setmode(old_mode);
  1080.                      fg_reset();
  1081.                   }
  1082.  
  1083. 130   Fastgraph User's Guide
  1084.  
  1085.  
  1086.  
  1087. Tandy/PCjr 16-color graphics mode
  1088.  
  1089.      In the Tandy/PCjr 16-color graphics mode (mode 9), each pixel can assume
  1090. a value between 0 and 15.  This means it takes four bits to represent a
  1091. pixel, so each byte of video memory holds two pixels.  Our triangle image is
  1092. nine pixels wide, so five bytes are needed for each row of the image.
  1093. Because the image is five pixels high, we need a bit map array of at least 25
  1094. bytes (five rows times five bytes per row) to hold the image.
  1095.  
  1096.      In video mode 9, it is fairly easy to develop the hexadecimal
  1097. representation of a bit map without first producing its binary equivalent.
  1098. This is because a pixel value and a hexadecimal digit each occupy four bits.
  1099. The triangle's hexadecimal representation for the 16-color Tandy/PCjr
  1100. graphics mode is shown below.  The pixels appearing in boldface represent the
  1101. actual image; the others are the filler values needed to complete each row of
  1102. the bit map after the ninth pixel.  We have chosen to display the perimeter
  1103. pixels in color 1 and the interior pixels in color 2.  Any pixel whose value
  1104. is zero is transparent and will thus leave the contents of video memory at
  1105. that position unchanged.
  1106.  
  1107.                             00   00   10   00   00
  1108.  
  1109.                             00   01   21   00   00
  1110.  
  1111.                             00   12   22   10   00
  1112.  
  1113.                             01   22   22   21   00
  1114.  
  1115.                             11   11   11   11   10
  1116.  
  1117.      Example 9-5 is similar to example 9-3, but it uses the Tandy/PCjr 16-
  1118. color graphics mode (mode 9) and its mode-specific bit map just constructed
  1119. to display the triangle.  The call to fg_drwimage produces a triangle with a
  1120. blue perimeter (color 1) and a green interior (color 2).
  1121.  
  1122.                                  Example 9-5.
  1123.  
  1124.                 char triangle[] = {
  1125.                    0x11,0x11,0x11,0x11,0x10,
  1126.                    0x01,0x22,0x22,0x21,0x00,
  1127.                    0x00,0x12,0x22,0x10,0x00,
  1128.                    0x00,0x01,0x21,0x00,0x00,
  1129.                    0x00,0x00,0x10,0x00,0x00
  1130.                    };
  1131.  
  1132.                 main()
  1133.                 {
  1134.                    int old_mode;
  1135.  
  1136.                    if (fg_testmode(9,1) == 0) {
  1137.                       printf("This program requires a 320 ");
  1138.                       printf("x 200 Tandy graphics mode.\n");
  1139.                       exit();
  1140.  
  1141.                                  Chapter 9:  Images and Image Management   131
  1142.  
  1143.  
  1144.                       }
  1145.  
  1146.                    old_mode = fg_getmode();
  1147.                    fg_setmode(9);
  1148.  
  1149.                    fg_setcolor(7);
  1150.                    fg_rect(0,319,0,199);
  1151.  
  1152.                    fg_move(156,101);
  1153.                    fg_drwimage(triangle,5,5);
  1154.                    fg_waitkey();
  1155.  
  1156.                    fg_setmode(old_mode);
  1157.                    fg_reset();
  1158.                 }
  1159.  
  1160.  
  1161. Hercules graphics modes
  1162.  
  1163.      The structure of the mode-specific bit maps for the Hercules graphics
  1164. modes (modes 11 and 12) is identical to two of the CGA graphics modes.  For
  1165. the standard Hercules graphics mode (mode 11), please refer to the discussion
  1166. of CGA two-color (mode 6) bit maps on page 128.  For the low-resolution
  1167. Hercules graphics mode (mode 12), please refer to the discussion of the CGA
  1168. four-color (mode 4) bit maps on page 127.
  1169.  
  1170. EGA and VGA graphics modes
  1171.  
  1172.      The structure of the mode-specific bit maps for the native EGA and VGA
  1173. graphics modes (modes 13 through 18) is identical to the mode-independent bit
  1174. map structure described in the previous section.  It thus makes no difference
  1175. if you use the fg_drwimage or fg_drawmap routines in these video modes.
  1176. Please refer to page 122 for a discussion of mode-independent bit maps.
  1177.  
  1178. MCGA and VGA 256-color graphics modes
  1179.  
  1180.      In the MCGA and VGA 256-color graphics modes (modes 19, 20, and 21),
  1181. each pixel can assume a value between 0 and 255 (FF hex).  This means it
  1182. takes eight bits to represent a pixel, or each byte of video memory holds one
  1183. pixel.  Our triangle image is nine pixels wide, so nine bytes are needed for
  1184. each row of the image.  Because the image is five pixels high, we need a bit
  1185. map array of at least 45 bytes (five rows times nine bytes per row) to hold
  1186. the image.  Note we will never need any filler bits in the 256-color video
  1187. modes.
  1188.  
  1189.      In the 256-color graphics video modes, it is quite simple to develop the
  1190. bit map for an image because each byte holds exactly one pixel.  The
  1191. triangle's hexadecimal representation for the 256-color graphics modes is
  1192. shown below.  As before, we have coded the perimeter pixels to be color 1 (01
  1193. hex) and the interior pixels to be color 2 (02 hex).  Any pixel whose value
  1194. is zero is transparent and will thus leave the contents of video memory at
  1195. that position unchanged.
  1196. 132   Fastgraph User's Guide
  1197.  
  1198.  
  1199.                   00   00   00   00   01   00   00   00   00
  1200.  
  1201.                   00   00   00   01   02   01   00   00   00
  1202.  
  1203.                   00   00   01   02   02   02   01   00   00
  1204.  
  1205.                   00   01   02   02   02   02   02   01   00
  1206.  
  1207.                   01   01   01   01   01   01   01   01   01
  1208.  
  1209.  
  1210.      Example 9-6 is also similar to example 9-3, but it uses the MCGA 256-
  1211. color graphics mode (mode 19) and its mode-specific bit map just constructed
  1212. to display the triangle.  The call to fg_drwimage produces a triangle with a
  1213. blue perimeter (color 1) and a green interior (color 2).
  1214.  
  1215.                                  Example 9-6.
  1216.  
  1217.                char triangle[] = {
  1218.                   0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  1219.                   0x00,0x01,0x02,0x02,0x02,0x02,0x02,0x01,0x00,
  1220.                   0x00,0x00,0x01,0x02,0x02,0x02,0x01,0x00,0x00,
  1221.                   0x00,0x00,0x00,0x01,0x02,0x01,0x00,0x00,0x00,
  1222.                   0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00
  1223.                   };
  1224.  
  1225.                main()
  1226.                {
  1227.                   int old_mode;
  1228.  
  1229.                   if (fg_testmode(19,1) == 0) {
  1230.                      printf("This program requires a 320 ");
  1231.                      printf("x 200 MCGA graphics mode.\n");
  1232.                      exit();
  1233.                      }
  1234.  
  1235.                   old_mode = fg_getmode();
  1236.                   fg_setmode(19);
  1237.  
  1238.                   fg_setcolor(7);
  1239.                   fg_rect(0,319,0,199);
  1240.  
  1241.                   fg_move(156,101);
  1242.                   fg_drwimage(triangle,9,5);
  1243.                   fg_waitkey();
  1244.  
  1245.                   fg_setmode(old_mode);
  1246.                   fg_reset();
  1247.                }
  1248.  
  1249.  
  1250.  
  1251. Text Modes
  1252.  
  1253.      You can also use the fg_drwimage routine to display images in text video
  1254. modes (modes 0, 1, 2, 3, and 7).  As one might expect, the image structure in
  1255.                                  Chapter 9:  Images and Image Management   133
  1256.  
  1257. the text modes is rather different from the graphics modes.  In chapter 5 we
  1258. saw that each character cell on the screen actually consists of a character
  1259. and an attribute.  The character value determines what character is
  1260. displayed, while the attribute value controls the character's appearance.
  1261. The structure of the attribute is:
  1262.  
  1263.  
  1264.                             bits  attribute
  1265.  
  1266.                             0-3   foreground color
  1267.                             4-6   background color
  1268.                              7    blinking
  1269.  
  1270.  
  1271.      The text mode image structure used with fg_drwimage also consists of a
  1272. series of characters and attributes.  For example, the following diagram
  1273. illustrates the structure of an image that is three characters wide and two
  1274. characters high.
  1275.  
  1276.  
  1277.                    char   attr   char   attr   char   attr
  1278.  
  1279.                    char   attr   char   attr   char   attr
  1280.  
  1281.  
  1282.      To illustrate the use of fg_drwimage in a text video mode, we'll display
  1283. the phrase "hello there" on two different lines in the center of the screen.
  1284. Furthermore, let's assume we would like the first character of each word to
  1285. appear in foreground color 1, the second in color 2, and so forth.  Our image
  1286. will consist of two lines each containing five characters, and each character
  1287. requires two bytes of storage (one for the character and another for its
  1288. attribute), so we'll need a 20-byte array for holding the image.  The array
  1289. really doesn't hold a bit map as in the graphics modes, so in the text modes
  1290. the first argument passed to fg_drwimage is instead called the image array.
  1291. In our example, the structure of the image array is:
  1292.  
  1293.  
  1294.            'h'    1    'e'    2    'l'    3    'l'    4    'o'    5
  1295.  
  1296.            't'    1    'h'    2    'e'    3    'r'    4    'e'    5
  1297.  
  1298.  
  1299. The subscript order that fg_drwimage uses for text modes is the same as for
  1300. the graphics modes.  For our five-row by two-column image, this means the
  1301. array subscripts would be numbered as follows:
  1302.  
  1303.  
  1304.               [10] [11] [12] [13] [14] [15] [16] [17] [18] [19]
  1305.  
  1306.                [0]  [1]  [2]  [3]  [4]  [5]  [6]  [7]  [8]  [9]
  1307.  
  1308.  
  1309.      Depending on the character and attribute values in the image array,
  1310. fg_drwimage can display new characters and attributes, new characters leaving
  1311. the existing attribute unchanged, new attributes leaving the existing
  1312. character unchanged, or leave both the existing character and attribute
  1313. 134   Fastgraph User's Guide
  1314.  
  1315. unchanged in video memory.  To keep an existing character or attribute,
  1316. simply specify a value of 0 in the corresponding element of the image array.
  1317. This capability is analogous to the fact that zero-valued pixels in graphics
  1318. mode bit maps leave video memory unchanged.
  1319.  
  1320.      Example 9-7 demonstrates the use of the fg_drwimage routine in the 80-
  1321. column color text mode (mode 3).  After establishing the video mode and
  1322. making the cursor invisible, the program calls fg_drwimage to display the
  1323. "hello there" image just discussed (note we pass the dimensions of the image
  1324. array as the number of bytes, not the number of characters).  The program
  1325. waits for a keystroke and then calls fg_drwimage again, passing a different
  1326. image array (called "image") of the same size.  This array changes the first
  1327. letter of both words from lower case to upper case (leaving the attribute
  1328. unchanged), and it makes the remaining characters have the same attribute as
  1329. the first character.  This is done in part by using zero-valued characters
  1330. and attributes to leave video memory unchanged.  After waiting for another
  1331. keystroke, the program exits.
  1332.                                  Example 9-7.
  1333.  
  1334.                     char hello[] = {
  1335.                        't',1, 'h',2, 'e',3, 'r',4, 'e',5,
  1336.                        'h',1, 'e',2, 'l',3, 'l',4, 'o',5
  1337.                        };
  1338.  
  1339.                     char image[] = {
  1340.                        'T',0, 0,1, 0,1, 0,1, 0,1,
  1341.                        'H',0, 0,1, 0,1, 0,1, 0,1
  1342.                        };
  1343.  
  1344.                     main()
  1345.                     {
  1346.                        int old_mode;
  1347.  
  1348.                        old_mode = fg_getmode();
  1349.                        fg_setmode(3);
  1350.                        fg_cursor(0);
  1351.  
  1352.                        fg_locate(12,37);
  1353.                        fg_drwimage(hello,10,2);
  1354.                        fg_waitkey();
  1355.  
  1356.                        fg_drwimage(image,10,2);
  1357.                        fg_waitkey();
  1358.  
  1359.                        fg_setmode(old_mode);
  1360.                        fg_reset();
  1361.                     }
  1362.  
  1363.  
  1364.  
  1365. Clipped Images
  1366.  
  1367.      The fg_drwimage routine displays an image without regard to the current
  1368. clipping limits.  If you want the image to be displayed with respect to the
  1369. clipping limits (as established by the most recent call to fg_setclip), you
  1370. should use the fg_clpimage routine instead of fg_drwimage.  Fg_clpimage takes
  1371. the same three arguments as fg_drwimage, and likewise displays the image such
  1372.                                  Chapter 9:  Images and Image Management   135
  1373.  
  1374. that its lower left corner is at the graphics cursor position.  Unlike
  1375. fg_drwimage, the fg_clpimage routine has no effect when used in a text video
  1376. mode.  Refer to pages 135 and 137 for example programs that use fg_clpimage.
  1377.  
  1378.      Because of the additional overhead involved in checking the clipping
  1379. limits, fg_clpimage is not as fast as fg_drwimage.  However, fg_clpimage does
  1380. attempt to reduce this overhead by temporarily extending the horizontal
  1381. clipping coordinates to a byte boundary in video memory.  For example, the
  1382. CGA two-color graphics mode (mode 6), the standard Hercules mode (mode 11),
  1383. and the native EGA and VGA graphics modes (modes 13 through 18) store eight
  1384. pixels in each byte of video memory.  This means the byte boundaries occur at
  1385. the horizontal coordinates 0, 8, 16, and so on.  The fg_clpimage routine will
  1386. extend the minimum horizontal (left) clipping limit to a byte boundary, and
  1387. it will extend the maximum horizontal (right) clipping limit to the last
  1388. pixel in a byte.  That is, if the left clipping limit is at x=10 and the
  1389. right clipping limit is at x=26, fg_clpimage will temporarily extend these
  1390. values to x=8 and x=31 respectively.  Please refer to page 158 for a further
  1391. discussion of byte boundaries.
  1392.  
  1393.  
  1394. Reversed Images
  1395.  
  1396.      The fg_revimage routine displays an image reversed (that is, mirrored
  1397. about the y-axis).  Fg_revimage takes the same three arguments as
  1398. fg_drwimage, and likewise displays the image such that its lower left corner
  1399. is at the graphics cursor position.  The fg_revimage routine has no effect
  1400. when used in a text video mode.  Refer to pages 135 and 137 for example
  1401. programs that use fg_revimage.
  1402.  
  1403.  
  1404. Reversed Clipped Images
  1405.  
  1406.      The fg_flpimage routine combines the effects of the fg_revimage and
  1407. fg_clpimage routines -- it displays a reversed image with respect to the
  1408. current clipping limits.  Fg_flpimage takes the same three arguments as
  1409. fg_drwimage, and likewise displays the image such that its lower left corner
  1410. is at the graphics cursor position.  Like the fg_clpimage routine,
  1411. fg_flpimage extends the clipping limits to a byte boundary to reduce the
  1412. overhead involved in clipping, and it has no effect when used in a text video
  1413. mode.  Refer to pages 135 and 137 for example programs that use fg_flpimage.
  1414.  
  1415.  
  1416. Some Examples
  1417.  
  1418.      Example 9-8 illustrates the use of the fg_drwimage, fg_clpimage,
  1419. fg_revimage, and fg_flpimage routines in the standard CGA four-color graphics
  1420. mode (mode 4).  The program uses each of these routines to display a small
  1421. white arrow, as shown in the pixel map below.
  1422.  
  1423.                              . . . . . . * . . .
  1424.                              . . . . . . * * . .
  1425.                              * * * * * * * * * .
  1426.                              * * * * * * * * * *
  1427.                              * * * * * * * * * .
  1428.                              . . . . . . * * . .
  1429.                              . . . . . . * . . .
  1430. 136   Fastgraph User's Guide
  1431.  
  1432. As before, we must first convert this image to a bit map.  The image is ten
  1433. pixels wide and seven high.  In mode 4, each pixel occupies two bits, so we
  1434. need a 21-byte array (7 rows by 3 columns) to store the image.  Since we want
  1435. to make the arrow white, each pixel will be displayed in color 3 (11 binary).
  1436. Here is the bit map and its hexadecimal equivalent for the arrow image in
  1437. mode 4 (the actual image is in boldface).
  1438.  
  1439.  
  1440.          00 00 00 00   00 00 11 00   00 00 00 00         00   0C   00
  1441.  
  1442.          00 00 00 00   00 00 11 11   00 00 00 00         00   0F   00
  1443.  
  1444.          11 11 11 11   11 11 11 11   11 00 00 00         FF   FF   C0
  1445.  
  1446.          11 11 11 11   11 11 11 11   11 11 00 00         FF   FF   F0
  1447.  
  1448.          11 11 11 11   11 11 11 11   11 00 00 00         FF   FF   C0
  1449.  
  1450.          00 00 00 00   00 00 11 11   00 00 00 00         00   0F   00
  1451.  
  1452.          00 00 00 00   00 00 11 00   00 00 00 00         00   0C   00
  1453.  
  1454.  
  1455.      After establishing the video mode, the program defines the clipping
  1456. region.  It then uses fg_drwimage to display the arrow pointing to the right
  1457. and fg_clpimage to do the same thing, but with respect to the clipping
  1458. limits.  Because the left edge of the arrow is displayed at x=10 and the
  1459. right clipping limit is at x=15, the call to fg_clpimage only draws the first
  1460. six columns of the arrow (that is, it does not draw the arrow head).
  1461.  
  1462.      Next, example 9-8 uses fg_revimage to display the arrow pointing to the
  1463. left.  To allow for the filler pixels, we must establish the graphics cursor
  1464. position two pixels to the left of the position used by fg_drwimage if we
  1465. want the tip of the left-pointing arrow to align with the tail of the right-
  1466. pointing arrow.  Finally, the program uses fg_flpimage to display an arrow
  1467. pointing to the left with regard to the clipping limits.  The call to
  1468. fg_flpimage displays the arrow head and the first two columns of the arrow
  1469. shaft.
  1470.  
  1471.                                  Example 9-8.
  1472.  
  1473.               char arrow[] = {
  1474.                  0x00,0x0C,0x00, 0x00,0x0F,0x00, 0xFF,0xFF,0xC0,
  1475.                  0xFF,0xFF,0xF0, 0xFF,0xFF,0xC0, 0x00,0x0F,0x00,
  1476.                  0x00,0x0C,0x00
  1477.                  };
  1478.  
  1479.               main()
  1480.               {
  1481.                  int old_mode;
  1482.  
  1483.                  if (fg_testmode(4,1) == 0) {
  1484.                     printf("This program requires a 320 ");
  1485.                     printf("x 200 CGA graphics mode.\n");
  1486.                     exit();
  1487.                     }
  1488.  
  1489.                                  Chapter 9:  Images and Image Management   137
  1490.  
  1491.                  old_mode = fg_getmode();
  1492.                  fg_setmode(4);
  1493.                  fg_setclip(0,15,0,199);
  1494.  
  1495.                  fg_move(10,10);
  1496.                  fg_drwimage(arrow,3,7);
  1497.                  fg_move(10,20);
  1498.                  fg_clpimage(arrow,3,7);
  1499.                  fg_move(8,30);
  1500.                  fg_revimage(arrow,3,7);
  1501.                  fg_move(8,40);
  1502.                  fg_flpimage(arrow,3,7);
  1503.                  fg_waitkey();
  1504.  
  1505.                  fg_setmode(old_mode);
  1506.                  fg_reset();
  1507.               }
  1508.  
  1509.  
  1510.      Example 9-9 is identical to example 9-8, but it uses the low resolution
  1511. EGA graphics mode (mode 13).  In the native EGA and VGA video modes, recall
  1512. that fg_drwimage uses the same mode-independent bit map format as the
  1513. fg_drawmap routine.  Thus we need a 14-byte array (7 rows by 2 columns) to
  1514. store the image.  Here is the bit map and its hexadecimal equivalent for the
  1515. arrow image in mode 13 (again, the actual image is in boldface).
  1516.  
  1517.               0 0 0 0 0 0 1 0   0 0 0 0 0 0 0 0         02   00
  1518.  
  1519.               0 0 0 0 0 0 1 1   0 0 0 0 0 0 0 0         03   00
  1520.  
  1521.               1 1 1 1 1 1 1 1   1 0 0 0 0 0 0 0         FF   80
  1522.  
  1523.               1 1 1 1 1 1 1 1   1 1 0 0 0 0 0 0         FF   C0
  1524.  
  1525.               1 1 1 1 1 1 1 1   1 0 0 0 0 0 0 0         FF   80
  1526.  
  1527.               0 0 0 0 0 0 1 1   0 0 0 0 0 0 0 0         03   00
  1528.  
  1529.               0 0 0 0 0 0 1 0   0 0 0 0 0 0 0 0         02   00
  1530.  
  1531.      Here is the program to display the arrows in mode 13.  Note that we must
  1532. define the color associated with the bit map in the native EGA and VGA
  1533. graphics modes with a call to the fg_setcolor routine (the other video modes
  1534. implicitly define the pixel colors in the bit map itself).
  1535.  
  1536.                                  Example 9-9.
  1537.  
  1538.                 char arrow[] = {
  1539.                    0x02,0x00, 0x03,0x00, 0xFF,0x80, 0xFF,0xC0,
  1540.                    0xFF,0x80, 0x03,0x00, 0x02,0x00
  1541.                    };
  1542.  
  1543.                 main()
  1544.                 {
  1545.                    int old_mode;
  1546.  
  1547. 138   Fastgraph User's Guide
  1548.  
  1549.  
  1550.                    if (fg_testmode(13,1) == 0) {
  1551.                       printf("This program requires a 320 ");
  1552.                       printf("x 200 EGA graphics mode.\n");
  1553.                       exit();
  1554.                       }
  1555.  
  1556.                    old_mode = fg_getmode();
  1557.                    fg_setmode(13);
  1558.                    fg_setclip(0,15,0,199);
  1559.                    fg_setcolor(15);
  1560.  
  1561.                    fg_move(10,10);
  1562.                    fg_drwimage(arrow,2,7);
  1563.                    fg_move(10,20);
  1564.                    fg_clpimage(arrow,2,7);
  1565.                    fg_move(4,30);
  1566.                    fg_revimage(arrow,2,7);
  1567.                    fg_move(4,40);
  1568.                    fg_flpimage(arrow,2,7);
  1569.                    fg_waitkey();
  1570.  
  1571.                    fg_setmode(old_mode);
  1572.                    fg_reset();
  1573.                 }
  1574.  
  1575.  
  1576.  
  1577. Pixel Run Maps
  1578.  
  1579.      The bit maps used with the fg_drawmap, fg_drwimage, and related routines
  1580. can consume array space quite rapidly.  This is especially true if the image
  1581. is large or contains many colors.  For example, a mode-independent bit-mapped
  1582. image that occupies the entire screen in a 320 by 200 graphics mode requires
  1583. 8,000 bytes of space per color.  Fastgraph provides another mode-independent
  1584. image format called pixel run maps, which are more efficient in terms of
  1585. space.  In pixel run maps, you store the entire image in a single array.
  1586. Pixel run maps are particularly useful for displaying static images such as
  1587. backgrounds.
  1588.  
  1589.      Let's return to our familiar triangle example and show how we could use
  1590. a pixel run map to display it.
  1591.  
  1592.                               . . . . * . . . .
  1593.                               . . . * x * . . .
  1594.                               . . * x x x * . .
  1595.                               . * x x x x x * .
  1596.                               * * * * * * * * *
  1597.  
  1598. As before, the pixels indicated by an asterisk (*) are the triangle's
  1599. perimeter, while those indicated by an x represent its interior points.  The
  1600. pixels shown as periods (.) are not part of the triangle itself, but they are
  1601. part of the pixel run map.
  1602.  
  1603.      If we start at the lower left corner of the image and proceed to the
  1604. right, we could represent the first row of the image as nine pixels of color
  1605. "asterisk".  Such a group of consecutive identically colored pixels is called
  1606.                                  Chapter 9:  Images and Image Management   139
  1607.  
  1608. a pixel run, so a single pixel run describes the first row of the image.  The
  1609. row above this one is a bit more complex.  It consists of five pixel runs:
  1610. one pixel of color "period", followed by one of color "asterisk", then five
  1611. of color "x", one of color "asterisk", and finally one of color "period".
  1612.  
  1613.      While we could construct separate pixel runs for each row of the image,
  1614. notice that three of the five rows in our triangle begin with the same color
  1615. pixel as the rightmost pixel in the previous row.  Fastgraph's pixel run map
  1616. format lets you take advantage of this property by allowing pixel runs to
  1617. wrap from one row to the next.  This means we can represent the pixel run of
  1618. color "period" extending from the right side of the second row to the left
  1619. side of the third row as a single run of three pixels.
  1620.  
  1621.      The Fastgraph routine fg_display displays an image stored as a pixel run
  1622. map.  The fg_display routine expects three arguments.  The first is an array
  1623. containing the pixel runs (passed by reference), the second is the number of
  1624. pixel runs in the array, and the third is the width in pixels of the image.
  1625. As with the other image display routines, the fg_display routine displays the
  1626. image such that its lower left corner is at the graphics cursor position on
  1627. the active video page.  The pixel run array is of the following format:
  1628.  
  1629.  
  1630.                              [0]   color for run 1
  1631.  
  1632.                              [1]   count for run 1
  1633.  
  1634.                              [2]   color for run 2
  1635.  
  1636.                              [3]   count for run 2
  1637.                               .
  1638.                               .
  1639.                               .
  1640.  
  1641.                           [2n-2]   color for run n
  1642.  
  1643.                           [2n-1]   count for run n
  1644.  
  1645.  
  1646. Each color is a value between 0 and 255 specifying the color index for that
  1647. pixel run.  Each count is a value between 0 and 255 specifying the length in
  1648. pixels of that pixel run.  If a run is longer than 255 pixels, it must be
  1649. broken into two or more runs.  For example, we could represent a pixel run of
  1650. length 265 as a run of length 255 followed by a run of length 10 of the same
  1651. color.  Note also the array space in bytes needed to store a pixel run map is
  1652. twice the number of runs.
  1653.  
  1654.      It requires 16 pixel runs to store our triangle image as a pixel run
  1655. map.  If we want to display the perimeter pixels in color 1, the interior
  1656. pixels in color 2, and the filler area in color 7, the pixel run map would
  1657. contain 16 sets of (color,count) pairs:  (1,9), (7,1), (1,1), (2,5), (1,1),
  1658. (7,3), (1,1), (2,3), (1,1), (7,5), (1,1), (2,1), (1,1), (7,7), (1,1), and
  1659. (7,4).
  1660.  
  1661.      Example 9-10 uses the fg_display routine to display the triangle as a
  1662. pixel run map in a 320 by 200 graphics mode.  The program displays the
  1663. triangle against a background of color 7, so the selection of color 7 for the
  1664. 140   Fastgraph User's Guide
  1665.  
  1666. filler area was important.  If some other color were chosen, the filler area
  1667. would not blend in with the background.
  1668.  
  1669.                                 Example 9-10.
  1670.  
  1671.                 char triangle[] = {
  1672.                    1,9, 7,1, 1,1, 2,5, 1,1, 7,3, 1,1, 2,3,
  1673.                    1,1, 7,5, 1,1, 2,1, 1,1, 7,7, 1,1, 7,4
  1674.                    };
  1675.  
  1676.                 main()
  1677.                 {
  1678.                    int old_mode, new_mode;
  1679.  
  1680.                    new_mode = fg_bestmode(320,200,1);
  1681.                    if (new_mode < 0 || new_mode == 12) {
  1682.                       printf("This program requires a 320 ");
  1683.                       printf("x 200 color graphics mode.\n");
  1684.                       exit();
  1685.                       }
  1686.  
  1687.                    old_mode = fg_getmode();
  1688.                    fg_setmode(new_mode);
  1689.  
  1690.                    fg_setcolor(7);
  1691.                    fg_rect(0,319,0,199);
  1692.  
  1693.                    fg_move(156,101);
  1694.                    fg_display(triangle,16,9);
  1695.                    fg_waitkey();
  1696.  
  1697.                    fg_setmode(old_mode);
  1698.                    fg_reset();
  1699.                 }
  1700.  
  1701.  
  1702.      If you have a pixel run map that only uses the first 16 color indices (0
  1703. to 15), you can use Fastgraph's packed pixel run map image format.  This
  1704. format packs two color values into each color byte and thus needs 25% less
  1705. array space to store an image.  The Fastgraph routine fg_displayp displays an
  1706. image stored as a packed pixel run map.  It is identical in all respects to
  1707. the fg_display routine except for the structure of the pixel run array.  Like
  1708. fg_display, the pixel run array used with fg_displayp is a list of pixel
  1709. runs, but two runs are packed into three bytes.  In each such set of three
  1710. bytes, the high four bits of the first byte contain the color of the first
  1711. run, and the low four bits contain the color of the second run.  The second
  1712. byte contains the length of the first run, and the third byte contains the
  1713. length of the second run.
  1714.  
  1715.      The following diagram illustrates the format of the pixel run array used
  1716. with the fg_displayp routine.  The image is assumed to contain n pixel runs,
  1717. where n is an even number.  If n is odd, the index of the last element is
  1718. 3n/2 (truncated) instead of 3n/2-1, and the low four bits of the last color
  1719. byte (that is, the color for pixel run n+1) are ignored.
  1720.                                  Chapter 9:  Images and Image Management   141
  1721.  
  1722.                         7                4   3                0
  1723.  
  1724.                    [0]    color for run 1     color for run 2
  1725.  
  1726.                    [1]              count for run 1
  1727.  
  1728.                    [2]              count for run 2
  1729.  
  1730.                    [3]    color for run 3     color for run 4
  1731.  
  1732.                    [4]              count for run 3
  1733.  
  1734.                    [5]              count for run 4
  1735.                     .
  1736.                     .
  1737.                     .
  1738.  
  1739.               [3n/2-3]   color for run n-1    color for run n
  1740.  
  1741.               [3n/2-2]             count for run n-1
  1742.  
  1743.               [3n/2-1]              count for run n
  1744.  
  1745.  
  1746.      The structure of the packed pixel run array allows for color values to
  1747. be between 0 and 15, and pixel run lengths to be between 0 and 255.  The
  1748. array space in bytes needed to store a packed pixel run map is 1.5 times the
  1749. number of runs, as compared to twice the number of runs for the standard
  1750. pixel run format.
  1751.  
  1752.      Example 9-11 is identical to example 9-10, but it uses fg_displayp
  1753. rather than fg_display to display the image.  Note the use of hexadecimal
  1754. numbers for defining the packed color values, which of course is not
  1755. necessary but certainly easier to read than expressing the quantities as
  1756. decimal numbers.
  1757.  
  1758.                                 Example 9-11.
  1759.  
  1760.                 char triangle[] = {
  1761.                    0x17,9,1, 0x12,1,5, 0x17,1,3, 0x12,1,3,
  1762.                    0x17,1,5, 0x12,1,1, 0x17,1,7, 0x17,1,4
  1763.                    };
  1764.  
  1765.                 main()
  1766.                 {
  1767.                    int old_mode, new_mode;
  1768.  
  1769.                    new_mode = fg_bestmode(320,200,1);
  1770.                    if (new_mode < 0 || new_mode == 12) {
  1771.                       printf("This program requires a 320 ");
  1772.                       printf("x 200 color graphics mode.\n");
  1773.                       exit();
  1774.                       }
  1775.  
  1776.                    old_mode = fg_getmode();
  1777.                    fg_setmode(new_mode);
  1778.  
  1779. 142   Fastgraph User's Guide
  1780.  
  1781.  
  1782.                    fg_setcolor(7);
  1783.                    fg_rect(0,319,0,199);
  1784.  
  1785.                    fg_move(156,101);
  1786.                    fg_displayp(triangle,16,9);
  1787.                    fg_waitkey();
  1788.  
  1789.                    fg_setmode(old_mode);
  1790.                    fg_reset();
  1791.                 }
  1792.  
  1793.      Both the fg_display and fg_displayp routines require the pixel run image
  1794. to be stored in an array.  In examples 9-10 and 9-11, the image is defined
  1795. within the program itself.  However, if the image is stored in a file, it
  1796. must first be read into the pixel run array.  Example 9-12 demonstrates this
  1797. procedure.  The program displays two images stored in files, one in standard
  1798. pixel run format and the other in packed pixel run format.  Each image is a
  1799. picture of the sea floor and some coral, as might be used for the background
  1800. in an aquarium.  The program runs in a 320 by 200 graphics mode, and the
  1801. image fills the entire screen.  It is assumed the image files contain the
  1802. list of pixel runs as a single byte stream that does not include embedded
  1803. characters such as carriage returns or line feeds.
  1804.  
  1805.      The first image, in standard pixel run format, is in the file coral.spr.
  1806. Note the program must open the file for reading in binary mode ("rb" in the
  1807. call to fopen).  The program reads the file's entire contents into the
  1808. pixel_runs array, whose size must be at least as large as the file size.
  1809. Because the image is stored in standard pixel run format, the number of pixel
  1810. runs is one-half the file size.  The program then uses the fg_move routine to
  1811. establish the lower left corner of the screen as the graphics cursor position
  1812. and then calls fg_display to display the image.  As mentioned earlier, the
  1813. image fills the entire screen, so its width is 320 pixels.
  1814.  
  1815.      After waiting for a keystroke, the program similarly displays the second
  1816. image.  This image is in the file coral.ppr and is stored in packed pixel run
  1817. format.  Because the image is packed, the number of pixel runs is two-thirds
  1818. the file size.  The program then clears the previous image from the screen
  1819. and calls fg_displayp to display the image.  After another keystroke, the
  1820. program restores the original video mode and screen attributes and returns to
  1821. DOS.
  1822.  
  1823.                                 Example 9-12.
  1824.  
  1825.              #include <stdio.h>
  1826.  
  1827.              char pixel_runs[20000];
  1828.  
  1829.              main()
  1830.              {
  1831.                 long filelength();
  1832.                 FILE *stream;
  1833.                 int file_size, run_count;
  1834.                 int old_mode, new_mode;
  1835.  
  1836.                 new_mode = fg_bestmode(320,200,1);
  1837.  
  1838.                                  Chapter 9:  Images and Image Management   143
  1839.  
  1840.                 if (new_mode < 0 || new_mode == 12) {
  1841.                    printf("This program requires a 320 ");
  1842.                    printf("x 200 color graphics mode.\n");
  1843.                    exit();
  1844.                    }
  1845.  
  1846.                 old_mode = fg_getmode();
  1847.                 fg_setmode(new_mode);
  1848.  
  1849.                 stream = fopen("coral.spr","rb");
  1850.                 file_size = (int)(filelength(fileno(stream)));
  1851.                 fread(pixel_runs,sizeof(char),file_size,stream);
  1852.                 fclose(stream);
  1853.                 run_count = file_size / 2;
  1854.                 fg_move(0,199);
  1855.                 fg_display(pixel_runs,run_count,320);
  1856.                 fg_waitkey();
  1857.  
  1858.                 stream = fopen("coral.ppr","rb");
  1859.                 file_size = (int)(filelength(fileno(stream)));
  1860.                 fread(pixel_runs,sizeof(char),file_size,stream);
  1861.                 fclose(stream);
  1862.                 run_count = file_size / 3 * 2;
  1863.                 fg_erase();
  1864.                 fg_displayp(pixel_runs,run_count,320);
  1865.                 fg_waitkey();
  1866.  
  1867.                 fg_setmode(old_mode);
  1868.                 fg_reset();
  1869.              }
  1870.  
  1871.  
  1872.      Another Fastgraph routine, fg_dispfile, displays an image directly from
  1873. a file.  This eliminates the need to read the file contents into an array
  1874. before displaying the image, and it also eliminates the need to compute the
  1875. number of pixel runs in the image.  The fg_dispfile routine can display
  1876. images stored in either standard or packed pixel run images.  The first of
  1877. its three arguments is the name of the file containing the image.  The file
  1878. name must be terminated with a null character, so QuickBASIC and FORTRAN
  1879. programmers will need to store a zero byte as the last character of the file
  1880. name string.  The second argument is the width in pixels of the image, and
  1881. the third argument defines the image format (that is, standard or packed).
  1882. As with fg_display and fg_displayp, the fg_dispfile routine displays the
  1883. image such that its lower left corner is at the graphics cursor position.
  1884.  
  1885.      Example 9-13 illustrates how to use the fg_dispfile routine to display
  1886. an image stored in a file.  It is functionally identical to example 9-12, but
  1887. it is much simpler because it uses fg_dispfile instead of fg_display and
  1888. fg_displayp to display the images.  The value of fg_dispfile's third argument
  1889. tells Fastgraph the image format.  A value of 0 indicates the file contains
  1890. an image in standard pixel run format, while a value of 1 indicates an image
  1891. in packed pixel run format.  As in example 9-12, the image files are assumed
  1892. to contain the list of pixel runs as a single byte stream that does not
  1893. include embedded characters such as carriage returns or line feeds.
  1894.  
  1895. 144   Fastgraph User's Guide
  1896.                                 Example 9-13.
  1897.  
  1898.                 main()
  1899.                 {
  1900.                    int old_mode, new_mode;
  1901.  
  1902.                    new_mode = fg_bestmode(320,200,1);
  1903.                    if (new_mode < 0 || new_mode == 12) {
  1904.                       printf("This program requires a 320 ");
  1905.                       printf("x 200 color graphics mode.\n");
  1906.                       exit();
  1907.                       }
  1908.  
  1909.                    old_mode = fg_getmode();
  1910.                    fg_setmode(new_mode);
  1911.  
  1912.                    fg_move(0,199);
  1913.                    fg_dispfile("coral.spr",320,0);
  1914.                    fg_waitkey();
  1915.  
  1916.                    fg_erase();
  1917.                    fg_dispfile("coral.ppr",320,1);
  1918.                    fg_waitkey();
  1919.  
  1920.                    fg_setmode(old_mode);
  1921.                    fg_reset();
  1922.                 }
  1923.  
  1924.      To display the image, the fg_dispfile routine attempts to allocate
  1925. enough dynamic memory to read the entire file.  If it is unable to do so, it
  1926. allocates the available memory and displays the image in more than one pass.
  1927. In either case, Fastgraph deallocates the memory after fg_dispfile displays
  1928. the image.
  1929.  
  1930.      The SNAPSHOT utility distributed with Fastgraph is a terminate and stay
  1931. resident program (TSR) that can capture graphics mode screen images and save
  1932. them in standard pixel run format files.  Thus, you can easily create files
  1933. with SNAPSHOT and display them with the fg_dispfile routine.  Appendix A
  1934. contains a complete description of the SNAPSHOT utility.
  1935.  
  1936.  
  1937. Display Patterns
  1938.  
  1939.      Examples 9-11, 9-12, and 9-13 work well in the graphics video modes with
  1940. 16 or 256 available colors.  However, in the four-color CGA graphics modes
  1941. the resulting image is not too good because of our limited color choices, and
  1942. it would look even worse in the Hercules graphics mode.  The Fastgraph
  1943. routine fg_pattern allows you to associate a dither pattern (actually, any
  1944. pixel sequence) with one of Fastgraph's 256 color indices appearing in a
  1945. pixel run map.  When displaying a pixel run map (with the fg_display,
  1946. fg_displayp, or fg_dispfile routine), Fastgraph will use the pattern
  1947. associated with that color index instead of displaying the color itself.
  1948.  
  1949.      The fg_pattern routine requires two integer arguments -- a color index
  1950. (between 0 and 255) and the display pattern defined for that color index.  A
  1951. display pattern's structure resembles the structure of video memory and is
  1952. thus dependent on the current video mode.  The following sections list the
  1953.                                  Chapter 9:  Images and Image Management   145
  1954.  
  1955. initial display patterns and explain how to construct new display patterns
  1956. for different graphics video modes.
  1957.  
  1958. CGA four-color graphics modes
  1959.  
  1960.      In the four-color CGA graphics modes (modes 4 and 5), the display
  1961. pattern is a 16-bit quantity consisting of an 8-bit shift count followed by
  1962. an 8-bit pixel pattern.  Each pixel assumes a value between 0 and 3, so the
  1963. pattern represents four pixels.  In even-numbered pixel rows, Fastgraph uses
  1964. the pixel pattern itself.  In odd-numbered pixel rows, Fastgraph rotates the
  1965. original pattern to the left by the number of bits specified by the shift
  1966. count.
  1967.  
  1968.      For example, if we are using the default CGA color palette, we could
  1969. create a lighter shade of cyan by alternating cyan pixels (color 1, 01
  1970. binary) with white pixels (color 3, 11 binary), as shown below.
  1971.  
  1972.  
  1973.                                  01 11 01 11
  1974.  
  1975.  
  1976. If we convert this pixel pattern to its hexadecimal equivalent, we obtain the
  1977. value 77.
  1978.  
  1979.      To complete the display pattern, we need to determine the shift count.
  1980. If we use a shift count of zero, the resulting display will simply be a
  1981. series of cyan and white vertical lines.  What we really need is a
  1982. checkerboard effect where a white pixel is above and below each cyan pixel,
  1983. and vice versa.  If we rotate the pattern one pixel (two bits) to the left,
  1984. we will obtain the desired effect.  That is, a shift count of two produces
  1985. the following pixel patterns:
  1986.  
  1987.                        even-numbered rows   01 11 01 11
  1988.                         odd-numbered rows   11 01 11 01
  1989.  
  1990. Combining the shift count with the pixel pattern yields the display pattern
  1991. 0277 hex.  The shift count is normally a multiple of two; note that a zero
  1992. shift count results in the same pattern being applied to all pixel rows.
  1993.  
  1994.      For the CGA four-color graphics modes, the fg_setmode routine
  1995. establishes the following initial display patterns:
  1996.  
  1997.  
  1998.                          color  shift count  hexadecimal
  1999.                          index  and pattern  equivalent
  2000.  
  2001.                            0    0 00000000   0000
  2002.                            1    0 01010101   0055
  2003.                            2    0 10101010   00AA
  2004.                            3    0 11111111   00FF
  2005.  
  2006.  
  2007. These values are repeated as necessary to define color indices 4 to 255.
  2008. That is, colors 4, 8, 12, ... , 252 use the same defaults as color 0.  Colors
  2009. 5, 9, 13, ... , 253 use the same defaults as color 1, and so forth.  Also
  2010. note that pattern 0000 represents four pixels of color 0, 0055 represents
  2011. 146   Fastgraph User's Guide
  2012.  
  2013. four pixels of color 1, 00AA represents four pixels of color 2, and 00FF
  2014. represents four pixels of color 3.
  2015.  
  2016. CGA two-color graphics mode
  2017.  
  2018.      In the two-color CGA graphics mode (mode 6), the display pattern is also
  2019. a 16-bit quantity consisting of an 8-bit shift count followed by an 8-bit
  2020. pixel pattern.  Each pixel assumes the value 0 or 1, so the pattern
  2021. represents eight pixels.  In even-numbered pixel rows, Fastgraph uses the
  2022. pixel pattern itself.  In odd-numbered pixel rows, Fastgraph rotates the
  2023. original pattern to the left by the number of bits specified by the shift
  2024. count.
  2025.  
  2026.      For example, we could create a lighter shade of white by alternating
  2027. black pixels (color 0) with white pixels (color 1), as shown below.
  2028.  
  2029.  
  2030.                                0 1 0 1 0 1 0 1
  2031.  
  2032.  
  2033. If we convert this pixel pattern to its hexadecimal equivalent, we obtain the
  2034. value 55.
  2035.  
  2036.      To complete the display pattern, we need to determine the shift count.
  2037. We must rotate the pattern one pixel (one bit) to the left to obtain the
  2038. checkerboard effect as in the CGA four color graphics modes.  That is, a
  2039. shift count of one produces the following pixel patterns:
  2040.  
  2041.                      even-numbered rows   0 1 0 1 0 1 0 1
  2042.                       odd-numbered rows   1 0 1 0 1 0 1 0
  2043.  
  2044. Combining the shift count with the pixel pattern yields the display pattern
  2045. 0155 hex.
  2046.  
  2047.      For the CGA two-color graphics mode, the fg_setmode routine establishes
  2048. the initial display patterns such that all even-numbered color indices are
  2049. assigned the value 0000, while all odd-numbered color indices are assigned
  2050. the value 00FF.  Note that pattern 0000 represents eight pixels of color 0,
  2051. and 00FF represents eight pixels of color 1.
  2052.  
  2053. Tandy/PCjr 16-color graphics mode
  2054.  
  2055.      In the Tandy/PCjr 16-color graphics mode (mode 9), the display pattern
  2056. is also 16-bit quantity consisting of an 8-bit shift count followed by an 8-
  2057. bit pixel pattern.  Each pixel assumes a value between 0 and 15, so the
  2058. pattern represents two pixels.  In even-numbered pixel rows, Fastgraph uses
  2059. the pixel pattern itself.  In odd-numbered pixel rows, Fastgraph rotates the
  2060. original pattern to the left by the number of bits specified by the shift
  2061. count.
  2062.  
  2063.      For example, we could create a lighter shade of blue by alternating blue
  2064. pixels (color 1, 0001 binary) with white pixels (color 15, 1111 binary), as
  2065. shown below.
  2066.  
  2067.  
  2068.                                   0001 1111
  2069.  
  2070.                                  Chapter 9:  Images and Image Management   147
  2071.  
  2072.  
  2073. If we convert this pixel pattern to its hexadecimal equivalent, we obtain the
  2074. value 1F.
  2075.  
  2076.      To complete the display pattern, we need to determine the shift count.
  2077. Using the same process as in the CGA graphics modes, we must rotate the
  2078. pattern one pixel (four bits) to the left to obtain the checkerboard effect.
  2079. That is, a shift count of four produces the following pixel patterns:
  2080.  
  2081.                         even-numbered rows   0001 1111
  2082.                          odd-numbered rows   1111 0001
  2083.  
  2084. Combining the shift count with the pixel pattern yields the display pattern
  2085. 041F hex.  The shift count is normally zero or four; note that a zero shift
  2086. count results in the same pattern being applied to all pixel rows.
  2087.  
  2088.      For the Tandy/PCjr 16-color graphics modes, the fg_setmode routine
  2089. establishes the initial display patterns such that color 0 is assigned the
  2090. value 0000 (two pixels of color 0), color 1 is assigned the value 0011 (two
  2091. pixels of color 1), color 2 is assigned the value 0022 (two pixels of color
  2092. 2), and so forth.  These values are repeated as necessary to define color
  2093. indices 16 to 255.  That is, colors 0, 16, 32, ... , 240 use the same
  2094. defaults as color 0.  Colors 1, 17, 33, ... , 241 use the same defaults as
  2095. color 1, and so forth.
  2096.  
  2097. Hercules graphics modes
  2098.  
  2099.      The structure of the display patterns for the Hercules graphics modes
  2100. (modes 11 and 12) is identical to two of the CGA graphics modes.  For the
  2101. standard Hercules graphics mode (mode 11), please refer to the discussion of
  2102. CGA two-color (mode 6) display patterns on page 146.  For the low-resolution
  2103. Hercules graphics mode (mode 12), please refer to the discussion of the CGA
  2104. four-color (mode 4) display patterns on page 145.
  2105.  
  2106. EGA graphics modes
  2107.  
  2108.      In the EGA graphics modes (modes 13 to 16), the display pattern is an 8-
  2109. bit quantity consisting of two 4-bit color values (for consistency with the
  2110. other video modes, we still pass the display pattern as a 16-bit quantity).
  2111. Each pixel assumes a value between 0 and 15 (0 and 5 in the EGA monochrome
  2112. graphics mode), so the pattern represents two pixels.  In even-numbered pixel
  2113. rows, Fastgraph uses the pixel pattern itself.  In odd-numbered pixel rows,
  2114. Fastgraph rotates the original pattern one pixel (four bits) to the left.
  2115.  
  2116.      For example, we could create a lighter shade of blue by alternating blue
  2117. pixels (color 1, 0001 binary) with white pixels (color 15, 1111 binary), as
  2118. shown below.
  2119.  
  2120.  
  2121.                                   0001 1111
  2122.  
  2123.  
  2124. If we convert this pixel pattern to its hexadecimal equivalent, we obtain the
  2125. value 1F.  The implied four-bit shift count produces the following pixel
  2126. patterns:
  2127. 148   Fastgraph User's Guide
  2128.  
  2129.                         even-numbered rows   0001 1111
  2130.                          odd-numbered rows   1111 0001
  2131.  
  2132. Extending the pixel pattern to a 16-bit quantify yields the display pattern
  2133. 001F hex.
  2134.  
  2135.      For the EGA and VGA 16-color graphics modes, the fg_setmode routine
  2136. establishes the initial display patterns such that color 0 is assigned the
  2137. value 0000 (two pixels of color 0), color 1 is assigned the value 0011 (two
  2138. pixels of color 1), color 2 is assigned the value 0022 (two pixels of color
  2139. 2), and so forth.  These values are repeated as necessary to define color
  2140. indices 16 to 255.  That is, colors 0, 16, 32, ... , 240 use the same
  2141. defaults as color 0.  Colors 1, 17, 33, ... , 241 use the same defaults as
  2142. color 1, and so forth.
  2143.  
  2144. MCGA/VGA 2-color graphics mode
  2145.  
  2146.      In the two-color MCGA/VGA graphics mode (mode 17), the display pattern
  2147. is a 2-bit quantity consisting of two 1-bit color values (for consistency
  2148. with the other video modes, we still pass the display pattern as a 16-bit
  2149. quantity).  Each pixel assumes the value 0 or 1, so the pattern represents
  2150. two pixels.  In even-numbered pixel rows, Fastgraph uses the pixel pattern
  2151. itself.  In odd-numbered pixel rows, Fastgraph rotates the original pattern
  2152. one pixel (one bit) to the left.
  2153.  
  2154.      For example, we could create a lighter shade of white by alternating
  2155. black pixels (color 0) with white pixels (color 1), as shown below.
  2156.  
  2157.  
  2158.                                      0 1
  2159.  
  2160.  
  2161. If we convert this pixel pattern to its hexadecimal equivalent, we obtain the
  2162. value 01.  The implied one-bit shift count produces the following pixel
  2163. patterns:
  2164.  
  2165.                            even-numbered rows   0 1
  2166.                             odd-numbered rows   1 0
  2167.  
  2168. Extending the pixel pattern to a 16-bit quantity yields the display pattern
  2169. 0001 hex.
  2170.  
  2171.      For the MCGA/VGA two-color graphics mode, the fg_setmode routine
  2172. establishes the initial display patterns such that all even-numbered color
  2173. indices are assigned the value 0000 (two pixels of color 0), while all odd-
  2174. numbered color indices are assigned the value 0003 (11 binary, or two pixels
  2175. of color 1).
  2176.  
  2177. VGA 16-color graphics mode
  2178.  
  2179.      The structure of the display patterns for the VGA 16-color graphics mode
  2180. (mode 18) is identical to that of the EGA graphics modes.  A discussion of
  2181. EGA display patterns appears on page 147.
  2182.                                  Chapter 9:  Images and Image Management   149
  2183.  
  2184. MCGA and VGA 256-color graphics modes
  2185.  
  2186.      The MCGA and VGA 256-color graphics modes (modes 19, 20, and 21) offer
  2187. 262,144 different colors, so dithering is seldom (if ever) required.  For
  2188. this reason, the fg_pattern routine has no effect in these video modes.
  2189.  
  2190. An example
  2191.  
  2192.      Example 9-14 illustrates the use of display patterns in several graphics
  2193. modes.  This program runs in any 320 by 200 color graphics mode and displays
  2194. the coral image in packed pixel run format, as in example 9-13, but it
  2195. redefines one or more of the color indices.  If the program runs in the
  2196. standard CGA four-color mode (mode 4), it redefines the first 16 display
  2197. patterns using the fg_pattern routine and the values in the CGApatterns
  2198. array.  In the Tandy/PCjr 16-color graphics mode (mode 9) and the EGA low-
  2199. resolution graphics mode (mode 13), the program redefines color index 15 to
  2200. produce an alternating gray and bright white dither pattern.  In the MCGA
  2201. 256-color mode (mode 19), display patterns are not available, so the program
  2202. uses fg_setrgb to define color index 15 as slightly darker shade of gray than
  2203. the default for color 7.
  2204.  
  2205.                                 Example 9-14.
  2206.  
  2207.                 int CGApatterns[] = {
  2208.                    0x0000,0x00FF,0x00FF,0x00FF,
  2209.                    0x02BB,0x0000,0x0222,0x0255,
  2210.                    0x00FF,0x00FF,0x00FF,0x0055,
  2211.                    0x00AA,0x00AA,0x00FF,0x0277
  2212.                    };
  2213.  
  2214.                 main()
  2215.                 {
  2216.                    int color;
  2217.                    int old_mode, new_mode;
  2218.  
  2219.                    new_mode = fg_bestmode(320,200,1);
  2220.                    if (new_mode < 0 || new_mode == 12) {
  2221.                       printf("This program requires a 320 ");
  2222.                       printf("x 200 color graphics mode.\n");
  2223.                       exit();
  2224.                       }
  2225.  
  2226.                    old_mode = fg_getmode();
  2227.                    fg_setmode(new_mode);
  2228.  
  2229.                    if (new_mode == 4) {
  2230.                       fg_palette(0,0);
  2231.                       for (color = 0; color < 16; color++)
  2232.                          fg_pattern(color,CGApatterns[color]);
  2233.                       }
  2234.                    else if (new_mode == 9 || new_mode == 13)
  2235.                       fg_pattern(15,0x04F7);
  2236.                    else
  2237.                       fg_setrgb(15,38,38,38);
  2238.  
  2239.                    fg_move(0,199);
  2240.  
  2241. 150   Fastgraph User's Guide
  2242.  
  2243.                    fg_dispfile("coral.ppr",320,1);
  2244.                    fg_waitkey();
  2245.  
  2246.                    fg_setmode(old_mode);
  2247.                    fg_reset();
  2248.                 }
  2249.  
  2250.  
  2251. Masking Maps
  2252.  
  2253.      When you are using the CGA, Tandy/PCjr, Hercules, or MCGA graphics video
  2254. modes, it is not possible to include color 0 pixels in an image displayed
  2255. with the fg_drwimage, fg_clpimage, fg_revimage, or fg_flpimage routines.
  2256. This is because these routines consider color 0 pixels to be transparent,
  2257. which means such pixels do not affect the corresponding pixels in video
  2258. memory.  There are times, however, when you will want color 0 pixels to be
  2259. destructive, or replace the video memory contents.
  2260.  
  2261.      Consider again the arrow image of example 9-8 (see page 135).  In this
  2262. example, we displayed a bright white (color 3) arrow against a black (color
  2263. 0) background in the standard CGA four-color graphics mode.  Suppose, though,
  2264. that we want to do just the opposite -- display a black (color 0) arrow
  2265. against a bright white (color 3) background.  Example 9-9 (see page 137) does
  2266. this in an EGA graphics mode, but how would we display the black arrow in a
  2267. CGA graphics mode?  We could of course use the fg_drawmap routine or one of
  2268. the routines for displaying pixel run maps, but fg_drawmap does not support
  2269. clipping or reversing an image.   There are, however, four Fastgraph routines
  2270. designed just for this purpose.  These routines are fg_drawmask, fg_clipmask,
  2271. fg_revmask, and fg_flipmask.
  2272.  
  2273.      Each of these routines uses a data structure called a masking map.  A
  2274. masking map is similar in structure to a pixel run map, but it does not
  2275. include any information about colors.  Instead, it consists of a series of
  2276. pixel runs that alternate between protected and unprotected pixels.  An
  2277. example might best clarify this.
  2278.  
  2279.      Once again, here is the arrow image of examples 9-8 and 9-9.
  2280.  
  2281.                              . . . . . . * . . .
  2282.                              . . . . . . * * . .
  2283.                              * * * * * * * * * .
  2284.                              * * * * * * * * * *
  2285.                              * * * * * * * * * .
  2286.                              . . . . . . * * . .
  2287.                              . . . . . . * . . .
  2288.  
  2289. This time, though, we want the arrow to appear in color 0.  Put another way,
  2290. we need the "period" pixels (.) to protect video memory, while we want the
  2291. "asterisk" pixels (*) to zero video memory.  Looking at this problem from the
  2292. perspective of a pixel run map, we have an alternating series of "protect"
  2293. and "zero" runs.  We don't need any information about pixel colors, just
  2294. whether to protect or to zero video memory.
  2295.  
  2296.      This is precisely the structure of a masking map.  Starting from the
  2297. lower left corner of the image and proceeding to the right, wrapping up to
  2298. the next row when needed, we could represent this image as a masking map with
  2299.                                  Chapter 9:  Images and Image Management   151
  2300.  
  2301. 6 protected pixels, 1 zeroed pixel, 9 protected pixels, 2 zeroed pixels, and
  2302. so on.  In general, the structure of a masking map is as follows.
  2303.  
  2304.  
  2305.                       [1]   length of 1st protect run
  2306.  
  2307.                       [2]   length of 1st  zero   run
  2308.  
  2309.                       [3]   length of 2nd protect run
  2310.  
  2311.                       [4]   length of 2nd  zero   run
  2312.                        .
  2313.                        .
  2314.                        .
  2315.  
  2316.                     [n-2]   length of final protect run
  2317.  
  2318.                     [n-1]   length of final  zero   run
  2319.  
  2320.  
  2321.      Looking at this diagram, we see that the even-numbered array elements
  2322. hold the length of the "protect" runs, and the odd-numbered elements hold the
  2323. length of the "zero" runs.  If you need the first run to be a "zero" run,
  2324. just include a "protect" run of length zero as the first element of the
  2325. array.  If the final run is a "protect" run, you do not need to include a
  2326. zero-length "zero" run at the end of the array.  Finally, if either type of
  2327. run exceeds 255 pixels, you'll need to split this into two or more pixel
  2328. runs.  In this case, be sure to include a zero-length run of the other type
  2329. between the two array elements.
  2330.  
  2331.      Example 9-15 illustrates the use of a masking map through the
  2332. fg_drawmask, fg_clipmask, fg_revmask, and fg_flipmask routines in the
  2333. standard CGA four-color graphics mode (mode 4) to draw a black (color 0)
  2334. arrow against a bright white background.  These four routines are
  2335. respectively analogous to the fg_drwimage, fg_clpimage, fg_revimage, and
  2336. fg_flpimage routines, but they use masking maps rather than bit maps.  The
  2337. first argument of each routine is the masking map array (passed by
  2338. reference), the second argument is the number of runs (that is, the number of
  2339. elements) in the masking map array, and the third argument is the width in
  2340. pixels of the image.
  2341.  
  2342.                                 Example 9-15.
  2343.  
  2344.                 char arrow[] = {6,1,9,2,2,9,1,19,7,2,8,1};
  2345.  
  2346.                 main()
  2347.                 {
  2348.                    int old_mode;
  2349.  
  2350.                    if (fg_testmode(4,1) == 0) {
  2351.                       printf("This program requires a 320 ");
  2352.                       printf("x 200 CGA graphics mode.\n");
  2353.                       exit();
  2354.                       }
  2355.  
  2356.                    old_mode = fg_getmode();
  2357.  
  2358. 152   Fastgraph User's Guide
  2359.  
  2360.                    fg_setmode(4);
  2361.                    fg_setclip(0,15,0,199);
  2362.  
  2363.                    fg_setcolor(3);
  2364.                    fg_rect(0,319,0,199);
  2365.  
  2366.                    fg_move(10,10);
  2367.                    fg_drawmask(arrow,12,10);
  2368.                    fg_move(10,20);
  2369.                    fg_clipmask(arrow,12,10);
  2370.                    fg_move(10,30);
  2371.                    fg_revmask(arrow,12,10);
  2372.                    fg_move(10,40);
  2373.                    fg_flipmask(arrow,12,10);
  2374.                    fg_waitkey();
  2375.  
  2376.                    fg_setmode(old_mode);
  2377.                    fg_reset();
  2378.                 }
  2379.  
  2380.      One of the more useful features of masking maps is the ability to clear
  2381. a portion of video memory before placing an image there.  This technique
  2382. provides an efficient, simple way to include color 0 pixels in an image.  It
  2383. is especially effective when displaying large or dithered images because the
  2384. masking map is typically much smaller than the bit map required by fg_drawmap
  2385. or its related routines.  Example 9-16 illustrates this process in the
  2386. standard CGA four-color graphics mode (mode 4) by displaying our arrow image
  2387. against a colored background.  In this example, the arrow has a bright white
  2388. (color 3) perimeter and a black (color 0) interior.
  2389.  
  2390.      The program displays the arrow in two steps.  It first uses fg_drawmask
  2391. to clear the video memory where the arrow will be displayed.  It then draws
  2392. the arrow's perimeter using the fg_drwimage routine.  The interior pixels in
  2393. the perimeter bit map are transparent, but since we just zeroed that video
  2394. memory, they appear in color 0.  Note we could improve this example by
  2395. creating a smaller masking map that only applies to the rectangle inscribing
  2396. the arrow's interior.  That is, we don't need to zero the video memory under
  2397. the arrow's perimeter because we will immediately display other pixels there.
  2398.  
  2399.                                 Example 9-16.
  2400.  
  2401.               char arrow_white[] = {
  2402.                  0x00,0x0C,0x00, 0x00,0x0F,0x00, 0xFF,0xFC,0xC0,
  2403.                  0xC0,0x00,0x30, 0xFF,0xFC,0xC0, 0x00,0x0F,0x00,
  2404.                  0x00,0x0C,0x00
  2405.                  };
  2406.  
  2407.               char arrow_black[] = {6,1,9,2,2,9,1,19,7,2,8,1};
  2408.  
  2409.               main()
  2410.               {
  2411.                  int old_mode;
  2412.  
  2413.                  if (fg_testmode(4,1) == 0) {
  2414.                     printf("This program requires a 320 ");
  2415.                     printf("x 200 CGA graphics mode.\n");
  2416.  
  2417.                                  Chapter 9:  Images and Image Management   153
  2418.  
  2419.                     exit();
  2420.                     }
  2421.  
  2422.                  old_mode = fg_getmode();
  2423.                  fg_setmode(4);
  2424.  
  2425.                  fg_setcolor(2);
  2426.                  fg_rect(0,319,0,199);
  2427.  
  2428.                  fg_move(10,10);
  2429.                  fg_drawmask(arrow_black,12,10);
  2430.                  fg_drwimage(arrow_white,3,7);
  2431.                  fg_waitkey();
  2432.  
  2433.                  fg_setmode(old_mode);
  2434.                  fg_reset();
  2435.               }
  2436.  
  2437.  
  2438.  
  2439. Retrieving Images
  2440.  
  2441.      Sometimes it is necessary to retrieve an image from video memory and
  2442. store it in one or more bit map arrays.  Fastgraph includes two routines,
  2443. fg_getmap and fg_getimage, for this purpose.  The fg_getmap routine retrieves
  2444. pixels of the current color index and stores them in the mode-independent bit
  2445. map format used by fg_drawmap.  The fg_getimage routine retrieves an image
  2446. and stores it in the mode-specific bit map format used by fg_drwimage,
  2447. fg_clpimage, fg_revimage, and fg_flpimage.  The arguments to fg_getmap and
  2448. fg_getimage are respectively analogous to those of fg_drawmap and
  2449. fg_drwimage:  the first is an array (passed by reference) to receive the bit
  2450. map, the second is the width of the bit map in bytes, and the last is the
  2451. height of the bit map in pixel rows.  With either routine, the graphics
  2452. cursor position on the active video page defines the lower left corner of the
  2453. image to retrieve.
  2454.  
  2455.      If we are using the fg_getmap routine (or the fg_getimage routine in any
  2456. of the native EGA and VGA graphics video modes) to retrieve an image
  2457. containing more than one color,  we must call the routine once per color.  In
  2458. this case we'll usually want to pass different bit map arrays to fg_getmap or
  2459. fg_getimage (or perhaps different offsets into the same array).  This might
  2460. seem unusual at first, but it parallels the behavior of the fg_drawmap and
  2461. fg_drwimage routines.  That is, to display a multicolor image using
  2462. fg_drawmap, we must call fg_drawmap once for each color in the image.  The
  2463. same holds true if we are using fg_drwimage in the native EGA and VGA
  2464. graphics video modes, because in these modes the mode-specific bit map
  2465. structure is identical to the mode-independent structure.
  2466.  
  2467.      Example 9-17 demonstrates a typical use of the fg_getmap routine.  The
  2468. program displays the word "text" in the upper left corner of the screen using
  2469. a 320 by 200 graphics mode.  It uses fg_getmap to retrieve the word as an
  2470. image and then displays it in a new position with the fg_drawmap routine.
  2471. Let's look at the program now, and afterward we'll more closely examine the
  2472. screen coordinates and the structure of the bit map array.
  2473.  
  2474. 154   Fastgraph User's Guide
  2475.                                 Example 9-17.
  2476.  
  2477.                 main()
  2478.                 {
  2479.                    char bitmap[32];
  2480.                    int old_mode, new_mode;
  2481.  
  2482.                    new_mode = fg_bestmode(320,200,1);
  2483.                    if (new_mode < 0 || new_mode == 12) {
  2484.                       printf("This program requires a 320 ");
  2485.                       printf("x 200 color graphics mode.\n");
  2486.                       exit();
  2487.                       }
  2488.  
  2489.                    old_mode = fg_getmode();
  2490.                    fg_setmode(new_mode);
  2491.  
  2492.                    fg_setcolor(9);
  2493.                    fg_text("text",4);
  2494.                    fg_waitkey();
  2495.  
  2496.                    fg_move(0,7);
  2497.                    fg_getmap(bitmap,4,8);
  2498.                    fg_move(4,15);
  2499.                    fg_drawmap(bitmap,4,8);
  2500.                    fg_waitkey();
  2501.  
  2502.                    fg_setmode(old_mode);
  2503.                    fg_reset();
  2504.                 }
  2505.  
  2506.      In all 320 by 200 graphics video modes, individual characters are 8
  2507. pixels wide and 8 pixels high.  This means the lower left corner of the (0,0)
  2508. character cell is referenced by the screen coordinates (0,7).  Hence, these
  2509. are the coordinates of the first call to fg_move.  The image retrieved in
  2510. example 9-17 is four characters long (32 pixels wide), so we need a bit map
  2511. array capable of holding 8 rows of 32 pixels (4 bytes) each.  Our bit map
  2512. array is therefore a 32-byte array, logically structured to have 4 columns
  2513. and 8 rows.  These values are the width and height arguments passed to
  2514. fg_getmap and fg_drawmap.
  2515.  
  2516.      After it retrieves the image, example 9-17 displays it one line below
  2517. and one-half character cell (four pixels) to the right of its original
  2518. position.  In other words, the program displays the image four pixels to the
  2519. right of the (1,0) character cell.  The lower left corner of that cell is
  2520. referenced by the screen coordinates (0,15), so the image should appear at
  2521. the position (4,15).  These are the coordinates of the second call to
  2522. fg_move.
  2523.  
  2524.      Example 9-18 illustrates the use of the fg_getmap and fg_drawmap
  2525. routines to retrieve and display two-color image.  This example is similar to
  2526. example 9-17, but this program first draws a rectangle in the upper left
  2527. corner of the screen and then displays the word "text" on top of the
  2528. rectangle in a different color.  Each character in a 320 by 200 graphics
  2529. video mode is 8 pixels wide and 8 pixels high, so the rectangle must be 32
  2530. pixels wide (4 characters times 8 pixels per character) and 8 pixels high.
  2531. The image to retrieve will be the same size as the rectangle.
  2532.                                  Chapter 9:  Images and Image Management   155
  2533.  
  2534.      The image retrieved in example 9-17 required a 32-byte array, logically
  2535. structured to have 4 columns and 8 rows.  Example 9-18 will retrieve an image
  2536. of the same structure, but the image contains two colors instead of just one.
  2537. This means we need two 32-byte arrays, one for each color, to hold the image.
  2538. We could instead use a single 64-byte array and pass an offset into that
  2539. array (specifically, &bitmap[32]) for processing the second color.
  2540.  
  2541.                                 Example 9-18.
  2542.  
  2543.                 main()
  2544.                 {
  2545.                    char bitmap1[32], bitmap2[32];
  2546.                    int old_mode, new_mode;
  2547.  
  2548.                    new_mode = fg_bestmode(320,200,1);
  2549.                    if (new_mode < 0 || new_mode == 12) {
  2550.                       printf("This program requires a 320 ");
  2551.                       printf("x 200 color graphics mode.\n");
  2552.                       exit();
  2553.                       }
  2554.  
  2555.                    old_mode = fg_getmode();
  2556.                    fg_setmode(new_mode);
  2557.  
  2558.                    fg_setcolor(7);
  2559.                    fg_rect(0,31,0,7);
  2560.                    fg_setcolor(9);
  2561.                    fg_text("text",4);
  2562.                    fg_waitkey();
  2563.  
  2564.                    fg_move(0,7);
  2565.                    fg_setcolor(7);
  2566.                    fg_getmap(bitmap1,4,8);
  2567.                    fg_setcolor(9);
  2568.                    fg_getmap(bitmap2,4,8);
  2569.  
  2570.                    fg_move(4,15);
  2571.                    fg_setcolor(7);
  2572.                    fg_drawmap(bitmap1,4,8);
  2573.                    fg_setcolor(9);
  2574.                    fg_drawmap(bitmap2,4,8);
  2575.                    fg_waitkey();
  2576.  
  2577.                    fg_setmode(old_mode);
  2578.                    fg_reset();
  2579.                 }
  2580.  
  2581.  
  2582.      Example 9-19 is similar to example 9-18, but it uses fg_getimage and
  2583. fg_drwimage instead of fg_getmap and fg_drawmap to retrieve and display the
  2584. image.  That is, it uses the mode-specific rather than the mode-independent
  2585. image retrieval and display routines.  When using the mode-specific routines,
  2586. the size of the bit map needed to hold the image depends on the video mode.
  2587. Furthermore, we must account for the fact that the native EGA and VGA
  2588. graphics video modes (mode 13 in this example) only retrieve and display one
  2589. color at a time.
  2590. 156   Fastgraph User's Guide
  2591.  
  2592.      For the CGA, Tandy/PCjr, and MCGA graphics video modes (modes 4, 9, and
  2593. 19), example 9-19 first computes the image width in bytes.  The image is 32
  2594. pixels wide.  In the CGA graphics mode, there are four pixels per byte of
  2595. video memory, so the image width is 8 bytes.  Similarly, there are two pixels
  2596. per byte in the Tandy/PCjr graphics mode, so in this mode the image width is
  2597. 16 bytes.  In the MCGA graphics mode, each byte holds one pixel, so the image
  2598. width is 32 bytes.  The image height is 8 pixels in all cases.  The size of
  2599. the bit map array is 256 bytes, which is the size required in the MCGA
  2600. graphics mode (32 bytes times 8 bytes).  The other video modes require less
  2601. storage, so in these modes only a portion of the bit map array will actually
  2602. be used.  The specified width is used in the calls to both fg_getimage and
  2603. fg_drwimage in these video modes.
  2604.  
  2605.      For the EGA graphics mode (mode 13), we must call fg_getimage and
  2606. fg_drwimage once for each of the two colors in the image.  As in example
  2607. 9-18, each color requires 32 bytes.  The first call to fg_getimage stores the
  2608. color 7 pixels in the first 32 bytes of the bit map array, and the second
  2609. call stores the color 9 pixels in the next 32 bytes.  Similarly, the first
  2610. call to fg_drwimage displays the color 7 pixels from the first 32 bytes of
  2611. the bit map array, while the second call displays the color 9 pixels from the
  2612. next 32 bytes.  Note how the most recent call to the fg_setcolor routine
  2613. determines which pixels are retrieved or the color of the displayed pixels.
  2614.  
  2615.                                 Example 9-19.
  2616.  
  2617.                 main()
  2618.                 {
  2619.                    char bitmap[256];
  2620.                    int old_mode, new_mode;
  2621.                    int width;
  2622.  
  2623.                    new_mode = fg_bestmode(320,200,1);
  2624.                    if (new_mode < 0 || new_mode == 12) {
  2625.                       printf("This program requires a 320 ");
  2626.                       printf("x 200 color graphics mode.\n");
  2627.                       exit();
  2628.                       }
  2629.  
  2630.                    if (new_mode == 4)
  2631.                       width = 8;
  2632.                    else if (new_mode == 9)
  2633.                       width = 16;
  2634.                    else if (new_mode == 19)
  2635.                       width = 32;
  2636.  
  2637.                    old_mode = fg_getmode();
  2638.                    fg_setmode(new_mode);
  2639.  
  2640.                    fg_setcolor(7);
  2641.                    fg_rect(0,31,0,7);
  2642.                    fg_setcolor(9);
  2643.                    fg_text("text",4);
  2644.                    fg_waitkey();
  2645.  
  2646.                    fg_move(0,7);
  2647.                    if (new_mode == 13) {
  2648.  
  2649.                                  Chapter 9:  Images and Image Management   157
  2650.  
  2651.                       fg_setcolor(7);
  2652.                       fg_getimage(bitmap,4,8);
  2653.                       fg_setcolor(9);
  2654.                       fg_getimage(&bitmap[32],4,8);
  2655.                       }
  2656.                    else
  2657.                       fg_getimage(bitmap,width,8);
  2658.  
  2659.                    fg_move(4,15);
  2660.                    if (new_mode == 13) {
  2661.                       fg_setcolor(7);
  2662.                       fg_drwimage(bitmap,4,8);
  2663.                       fg_setcolor(9);
  2664.                       fg_drwimage(&bitmap[32],4,8);
  2665.                       }
  2666.                    else
  2667.                       fg_drwimage(bitmap,width,8);
  2668.  
  2669.                    fg_waitkey();
  2670.  
  2671.                    fg_setmode(old_mode);
  2672.                    fg_reset();
  2673.                 }
  2674.  
  2675.      We can also use the fg_getimage routine to retrieve images in text video
  2676. modes.  In text modes, however, there are a few differences we must consider
  2677. when using fg_getimage.  First, the text cursor position, not the graphics
  2678. cursor position, specifies the lower left corner of the image.  Hence we must
  2679. use the fg_locate routine instead of fg_move to define the image location.
  2680. Second, the image width is always twice the number of characters per image
  2681. row (that is, for each character we have an character byte and an attribute
  2682. byte).  The fg_getmap routine has no effect when used in a text video mode.
  2683.  
  2684.      Example 9-20 shows a simple use of fg_getimage in text modes.  This
  2685. program is similar to example 9-19, but it runs in an 80-column text mode
  2686. rather than a 320 by 200 graphics mode.  As before, the program will retrieve
  2687. the four characters "text" as an image from the upper left corner of the
  2688. screen and then display it in a different location.  Because the image
  2689. consists of four characters in one row, the image width is 8 bytes and the
  2690. image height is 1.
  2691.  
  2692.                                 Example 9-20.
  2693.  
  2694.                    main()
  2695.                    {
  2696.                       int old_mode;
  2697.                       char image[8];
  2698.  
  2699.                       old_mode = fg_getmode();
  2700.  
  2701.                       if (fg_testmode(3,1))
  2702.                          fg_setmode(3);
  2703.                       else if (fg_testmode(7,1))
  2704.                          fg_setmode(7);
  2705.                       else {
  2706.                          printf("This program requires\n");
  2707.  
  2708. 158   Fastgraph User's Guide
  2709.  
  2710.                          printf("an 80-column display.\n");
  2711.                          exit();
  2712.                          }
  2713.  
  2714.                       fg_cursor(0);
  2715.                       fg_setattr(9,7,0);
  2716.                       fg_text("text",4);
  2717.                       fg_waitkey();
  2718.  
  2719.                       fg_locate(0,0);
  2720.                       fg_getimage(image,8,1);
  2721.                       fg_locate(1,1);
  2722.                       fg_drwimage(image,8,1);
  2723.                       fg_waitkey();
  2724.  
  2725.                       fg_setmode(old_mode);
  2726.                       fg_reset();
  2727.                    }
  2728.  
  2729.  
  2730. Byte Boundaries
  2731.  
  2732.      Video memory, like standard random-access memory, is divided into units
  2733. called bytes.  In text modes, each byte holds either a character or an
  2734. attribute.  In graphics modes, each byte of video memory holds one or more
  2735. horizontally contiguous pixels.  If two adjacent horizontal pixels are stored
  2736. in different bytes, then we say that a byte boundary exists between the two
  2737. pixels.
  2738.  
  2739.      The number of pixels per byte depends on the video mode being used, so
  2740. the byte boundaries also depend on the video mode.  That is, a byte boundary
  2741. in a CGA graphics mode is not necessarily a byte boundary in an EGA graphics
  2742. mode.  The following table summarizes the number of pixels per byte of video
  2743. memory and the byte boundary sequences for each supported graphics video
  2744. mode.  Note that any horizontal coordinate whose value is a multiple of 8 is
  2745. always a byte boundary, regardless of the video mode.
  2746.                                  Chapter 9:  Images and Image Management   159
  2747.  
  2748.  
  2749.                    mode   pixels   horizontal coordinates
  2750.                   number per byte    of byte boundaries
  2751.  
  2752.  
  2753.                     4       4      0, 4,  8, 12, ... , 316
  2754.                     5       4      0, 4,  8, 12, ... , 316
  2755.                     6       8      0, 8, 16, 24, ... , 632
  2756.                     9       2      0, 2,  4,  6, ... , 318
  2757.                     11      8      0, 8, 16, 24, ... , 712
  2758.                     12      4      0, 4,  8, 12, ... , 316
  2759.                     13      8      0, 8, 16, 24, ... , 312
  2760.                     14      8      0, 8, 16, 24, ... , 632
  2761.                     15      8      0, 8, 16, 24, ... , 632
  2762.                     16      8      0, 8, 16, 24, ... , 632
  2763.                     17      8      0, 8, 16, 24, ... , 632
  2764.                     18      8      0, 8, 16, 24, ... , 632
  2765.                     19      1      0, 1,  2,  3, ... , 319
  2766.                     20      1      0, 1,  2,  3, ... , 319
  2767.                     21      1      0, 1,  2,  3, ... , 319
  2768.  
  2769.  
  2770.  
  2771.  
  2772. Image Transfer Routines
  2773.  
  2774.      The Fastgraph routines described in this section transfer images between
  2775. areas of video memory.  These routines are often used in animation sequences
  2776. requiring high-performance graphics, so they must be as efficient as
  2777. possible.  To this end, Fastgraph will force their horizontal pixel
  2778. coordinates to byte boundaries, which eliminates the need to process any
  2779. pixels individually.  Fastgraph accomplishes this by reducing minimum
  2780. horizontal coordinates to a byte boundary and extending maximum horizontal
  2781. coordinates to the last pixel in a video memory byte.  Note that since we are
  2782. talking about pixel coordinates and not character cells, the coordinate
  2783. modification only occurs in graphics video modes.
  2784.  
  2785.      An example might best help explain this important feature.  The CGA
  2786. four-color graphics modes (modes 4 and 5) store four pixels in each byte of
  2787. video memory.  This means the byte boundaries occur at multiples of four
  2788. pixels.  Thus, when you use the image transfer routines in modes 4 and 5,
  2789. Fastgraph reduces minimum x coordinates to the next lower multiple of four.
  2790. Similarly, it extends their maximum x coordinates to the next higher multipleof four, less one pixel.  That is, if a minimum x coordinate is 7 and a
  2791.  
  2792. 160   Fastgraph User's Guide
  2793.  
  2794. maximum x coordinate is 30, Fastgraph will modify these values to 4 and 31
  2795. respectively.  If the x coordinates were originally 4 and 31, Fastgraph would
  2796. leave them unchanged.  Note, too, that because each pixel in the MCGA and VGA
  2797. 256-color graphics modes (modes 19, 20, and 21) occupies a separate byte of
  2798. video memory, Fastgraph does not need to modify horizontal coordinates in
  2799. these video modes.
  2800.  
  2801.      Several of Fastgraph's image transfer routines reference a video page
  2802. called the hidden page.  The Fastgraph routine fg_sethpage defines which
  2803. video page will be used as the hidden page.  This routine takes as its only
  2804. argument an integer value specifying the hidden page number.  If you are
  2805. using a virtual video page for the hidden page, you must call the fg_sethpage
  2806. routine after allocating that page.  There is also a routine named
  2807. fg_gethpage that returns the hidden page number, as specified in the most
  2808. recent call to fg_sethpage, as its function value.  The fg_gethpage routine
  2809. takes no arguments.
  2810.  
  2811.      Fastgraph's simplest image transfer routines are fg_save and fg_restore.
  2812. The fg_save routine transfers a rectangular region from the active video page
  2813. (as defined in the most recent call to fg_setpage) to the same position on
  2814. the hidden video page (as defined in the most recent call to fg_sethpage).
  2815. The fg_restore routine performs the complementary task -- it transfers a
  2816. rectangular region from the hidden page to the active page.  Each of these
  2817. routines requires four arguments that define the coordinates of the region to
  2818. transfer, in the order minimum x, maximum x, minimum y, and maximum y.  In
  2819. text modes, the coordinates are expressed as character space quantities (rows
  2820. and columns).  In graphics modes, they are expressed as screen space values
  2821. (pixels); the x coordinates are extended to byte boundaries if required.
  2822. There are also world space versions of these routines named fg_savew and
  2823. fg_restorew available in graphics modes.
  2824.  
  2825.      Example 9-21 demonstrates the use of Fastgraph's fg_save, fg_restore,
  2826. and fg_sethpage routines in an 80-column text video mode.  After establishing
  2827. the video mode (note the calls to fg_testmode specify that two video pages
  2828. are needed), the program fills the screen with text and then waits for a
  2829. keystroke.  Following this, the program displays a small pop-up window
  2830. prompting for another keystroke.  After waiting for the second keystroke, the
  2831. program erases the pop-up window by restoring the original screen contents,
  2832. and then waits for yet another keystroke before returning to DOS.  We'll
  2833. present the program now, and afterward analyze it in detail.
  2834.  
  2835.                                 Example 9-21.
  2836.  
  2837.                 main()
  2838.                 {
  2839.                    int row;
  2840.                    int old_mode;
  2841.                    char string[17];
  2842.  
  2843.                    old_mode = fg_getmode();
  2844.  
  2845.                    if (fg_testmode(3,2))
  2846.                       fg_setmode(3);
  2847.                    else if (fg_testmode(7,2))
  2848.                       fg_setmode(7);
  2849.                    else {
  2850.  
  2851.                                  Chapter 9:  Images and Image Management   161
  2852.  
  2853.                       printf("This program requires\n");
  2854.                       printf("an 80-column display.\n");
  2855.                       exit();
  2856.                       }
  2857.  
  2858.                    fg_cursor(0);
  2859.                    fg_setattr(9,7,0);
  2860.  
  2861.                    for (row = 0; row < 25; row++) {
  2862.                       sprintf(string," This is row %2d ",row);
  2863.                       fg_locate(row,0);
  2864.                       fg_text(string,16);
  2865.                       fg_text(string,16);
  2866.                       fg_text(string,16);
  2867.                       fg_text(string,16);
  2868.                       fg_text(string,16);
  2869.                       }
  2870.                    fg_waitkey();
  2871.  
  2872.                    fg_allocate(1);
  2873.                    fg_sethpage(1);
  2874.                    fg_save(32,47,11,13);
  2875.                    fg_setcolor(1);
  2876.                    fg_rect(32,47,11,13);
  2877.                    fg_setattr(15,1,0);
  2878.                    fg_locate(12,33);
  2879.                    fg_text("Press any key.",14);
  2880.                    fg_waitkey();
  2881.  
  2882.                    fg_restore(32,47,11,13);
  2883.                    fg_waitkey();
  2884.  
  2885.                    fg_freepage(1);
  2886.                    fg_setmode(old_mode);
  2887.                    fg_reset();
  2888.                 }
  2889.  
  2890.      Example 9-21 first establishes the video mode and uses the fg_cursor
  2891. routine to make the BIOS cursor invisible.  It then executes a for loop that
  2892. fills each row of the screen with the phrase "This is row n", where n is the
  2893. row number (between 0 and 24).  Next, the program uses the fg_allocate
  2894. routine to create video page 1 as a virtual video page.  This is needed in
  2895. case the program is running in mode 7, which has only one true page (if the
  2896. program is running in mode 3, the call to fg_allocate has no effect).  The
  2897. program then makes page 1 the hidden page by calling the fg_sethpage routine.
  2898.  
  2899.      After setting up the hidden video page, but before displaying the pop-up
  2900. window, example 9-21 uses the fg_save routine to save the current contents of
  2901. the area that the pop-up window will replace.  The fg_save routine copies
  2902. this region to the hidden page.  The program then displays the pop-up window
  2903. in the middle of the screen and leaves it there until a key is pressed.
  2904. Following this, the program uses the fg_restore routine to replace the pop-up
  2905. window with the original contents of that region.  This effectively erases
  2906. the pop-up window and restores the original screen.  The program then waits
  2907. for another keystroke, after which it releases the virtual page and returns
  2908. to DOS.
  2909.  
  2910. 162   Fastgraph User's Guide
  2911.  
  2912.  
  2913.      The next example, 9-22, is similar to example 9-21, but it runs in a 320
  2914. by 200 color graphics mode instead of a text mode.  The main differences
  2915. between this program and example 9-21 are the use of 40-column text and the
  2916. use of screen space coordinates instead of character space coordinates in the
  2917. calls to fg_save, fg_restore, and fg_rect.  Note that the call to fg_allocate
  2918. creates a virtual page if the program is running in modes 4, 9, or 19.  In
  2919. mode 13, which has 8 true pages, the fg_allocate routine does nothing.
  2920.  
  2921.                                 Example 9-22.
  2922.  
  2923.               main()
  2924.               {
  2925.                  int row;
  2926.                  int new_mode, old_mode;
  2927.                  char string[21];
  2928.  
  2929.                  new_mode = fg_bestmode(320,200,2);
  2930.                  if (new_mode < 0 || new_mode == 12) {
  2931.                     printf("This program requires a 320 ");
  2932.                     printf("x 200 color graphics mode.\n");
  2933.                     exit();
  2934.                     }
  2935.                  old_mode = fg_getmode();
  2936.                  fg_setmode(new_mode);
  2937.  
  2938.                  fg_setcolor(7);
  2939.                  fg_rect(0,319,0,199);
  2940.                  fg_setcolor(9);
  2941.  
  2942.                  for (row = 0; row < 25; row++) {
  2943.                     sprintf(string,"   This is row %2d   ",row);
  2944.                     fg_locate(row,0);
  2945.                     fg_text(string,20);
  2946.                     fg_text(string,20);
  2947.                     }
  2948.                  fg_waitkey();
  2949.  
  2950.                  fg_allocate(1);
  2951.                  fg_sethpage(1);
  2952.                  fg_save(96,223,88,111);
  2953.                  fg_setcolor(1);
  2954.                  fg_rect(96,223,88,111);
  2955.                  fg_setcolor(15);
  2956.                  fg_locate(12,13);
  2957.                  fg_text("Press any key.",14);
  2958.                  fg_waitkey();
  2959.  
  2960.                  fg_restore(96,223,88,111);
  2961.                  fg_waitkey();
  2962.  
  2963.                  fg_freepage(1);
  2964.                  fg_setmode(old_mode);
  2965.                  fg_reset();
  2966.               }
  2967.  
  2968.                                  Chapter 9:  Images and Image Management   163
  2969.  
  2970.      The fg_save and fg_restore routines each copy a rectangular region from
  2971. one video page to the same position on another video page.  What if you need
  2972. to copy the region to a different position on another video page, or perhaps
  2973. copy it elsewhere on the same video page?  Fastgraph provides a more general
  2974. image transfer routine named fg_transfer.  The fg_transfer routine copies a
  2975. rectangular region on any video page to any position on any video page.  Like
  2976. fg_save and fg_restore, the fg_transfer routine works in text and graphics
  2977. video modes.  In graphics modes, fg_transfer extends its x coordinates to
  2978. byte boundaries if necessary.
  2979.  
  2980.      The fg_transfer routine requires eight integer arguments.  The first
  2981. four arguments define the region to copy, in the same order as expected by
  2982. the fg_save and fg_restore routines.  The next two arguments define the lower
  2983. left corner of the image destination, while the final two arguments
  2984. respectively specify the source and destination video page numbers.  In
  2985. short, fg_transfer copies the specified region from the source page to the
  2986. specified position on the destination page.
  2987.  
  2988.      Example 9-23 is identical to example 9-21, but it uses fg_transfer
  2989. rather than fg_save and fg_restore.  We have arbitrarily chosen to copy the
  2990. region overwritten by the pop-up window to the lower left corner of the
  2991. hidden page (page 1).  When we copy this region back to the visual page, we
  2992. copy from the lower left corner of the hidden page back to the original
  2993. position on the visual page (page 0).  This sequence is shown in the
  2994. following diagram.
  2995.  
  2996.              (11,32)    (11,47)               (22,0)     (22,15)
  2997.               +--------------+  first call     +--------------+
  2998.               |This is row 11|  ------------>  |This is row 11|
  2999.               |This is row 12|                 |This is row 12|
  3000.               |This is row 13|  <------------  |This is row 13|
  3001.               +--------------+    second call  +--------------+
  3002.              (13,32)    (13,47)               (24,0)     (24,15)
  3003.  
  3004.                 visual page (0)                  hidden page (1)
  3005.  
  3006. To copy one region to a new position and then back to its original position,
  3007. note how we make the fifth and sixth arguments in the first call to
  3008. fg_transfer the same values as the first and fourth arguments in the second
  3009. call.  Similarly, the fifth and sixth arguments in the second call must be
  3010. the same as the first and fourth arguments in the first call.  Now, here is
  3011. example 9-23.
  3012.  
  3013.                                 Example 9-23.
  3014.  
  3015.                 main()
  3016.                 {
  3017.                    int row;
  3018.                    int old_mode;
  3019.                    char string[17];
  3020.  
  3021.                    old_mode = fg_getmode();
  3022.  
  3023.                    if (fg_testmode(3,2))
  3024.                       fg_setmode(3);
  3025.                    else if (fg_testmode(7,2))
  3026.  
  3027. 164   Fastgraph User's Guide
  3028.  
  3029.                       fg_setmode(7);
  3030.                    else {
  3031.                       printf("This program requires\n");
  3032.                       printf("an 80-column display.\n");
  3033.                       exit();
  3034.                       }
  3035.  
  3036.                    fg_cursor(0);
  3037.                    fg_setattr(9,7,0);
  3038.  
  3039.                    for (row = 0; row < 25; row++) {
  3040.                       sprintf(string," This is row %2d ",row);
  3041.                       fg_locate(row,0);
  3042.                       fg_text(string,16);
  3043.                       fg_text(string,16);
  3044.                       fg_text(string,16);
  3045.                       fg_text(string,16);
  3046.                       fg_text(string,16);
  3047.                       }
  3048.                    fg_waitkey();
  3049.  
  3050.                    fg_allocate(1);
  3051.                    fg_transfer(32,47,11,13,0,24,0,1);
  3052.                    fg_setcolor(1);
  3053.                    fg_rect(32,47,11,13);
  3054.                    fg_setattr(15,1,0);
  3055.                    fg_locate(12,33);
  3056.                    fg_text("Press any key.",14);
  3057.                    fg_waitkey();
  3058.  
  3059.                    fg_transfer(0,15,22,24,32,13,1,0);
  3060.                    fg_fg_waitkey();
  3061.  
  3062.                    fg_freepage(1);
  3063.                    fg_setmode(old_mode);
  3064.                    fg_reset();
  3065.                 }
  3066.  
  3067.  
  3068.      Example 9-24 illustrates another use of the fg_transfer routine.  This
  3069. example is functionally identical to example 9-17 (see page 153), but it uses
  3070. fg_transfer instead of fg_getmap and fg_drawmap.  By using the fg_transfer
  3071. routine, we eliminate the calls to fg_getmap and fg_drawmap, the two calls to
  3072. fg_move, and the 32-byte array needed to retrieve the image.  As an added
  3073. bonus, using fg_transfer is much faster than the technique of example 9-17,
  3074. although we probably won't notice this gain with such a small image.
  3075.  
  3076.      The image copied in example 9-24 is one row of four characters, so its
  3077. width in screen space is 32 pixels and its height is 8 pixels.  Because the
  3078. image is in the upper left corner of the screen, the image boundaries are
  3079. xmin=0, xmax=31, ymin=0, and ymax=7.  We want to move the image one-half
  3080. character cell (4 pixels) to the right and one row (8 pixels) down, so our
  3081. destination coordinates are x=4 (xmin+4) and y=15 (ymax+8).  Also, we are
  3082. copying the image from one position to another on the visual page, so both
  3083. the source and destination pages are 0.
  3084.  
  3085.                                  Chapter 9:  Images and Image Management   165
  3086.                                 Example 9-24.
  3087.  
  3088.                 main()
  3089.                 {
  3090.                    int old_mode, new_mode;
  3091.  
  3092.                    new_mode = fg_bestmode(320,200,1);
  3093.                    if (new_mode < 0 || new_mode == 12) {
  3094.                       printf("This program requires a 320 ");
  3095.                       printf("x 200 color graphics mode.\n");
  3096.                       exit();
  3097.                       }
  3098.  
  3099.                    old_mode = fg_getmode();
  3100.                    fg_setmode(new_mode);
  3101.  
  3102.                    fg_setcolor(9);
  3103.                    fg_text("text",4);
  3104.                    fg_waitkey();
  3105.  
  3106.                    fg_transfer(0,31,0,7,4,15,0,0);
  3107.                    fg_waitkey();
  3108.  
  3109.                    fg_setmode(old_mode);
  3110.                    fg_reset();
  3111.                 }
  3112.  
  3113.      Example 9-25 shows yet another application of the fg_transfer routine in
  3114. a graphics video mode.  The program displays a rectangle in the upper left
  3115. quadrant of the screen and then centers the word "quadrant" inside the
  3116. rectangle.  After waiting for a keystroke, the program uses fg_transfer to
  3117. first copy the upper left quadrant to the upper right quadrant.  It then uses
  3118. fg_transfer again to copy the upper half of the screen to the lower half.
  3119. The end result of this is the screen being filled with what was originally in
  3120. the upper left quadrant.
  3121.  
  3122.                                 Example 9-25.
  3123.  
  3124.                 main()
  3125.                 {
  3126.                    int new_mode, old_mode;
  3127.  
  3128.                    new_mode = fg_bestmode(320,200,1);
  3129.                    if (new_mode < 0 || new_mode == 12) {
  3130.                       printf("This program requires a 320 ");
  3131.                       printf("x 200 color graphics mode.\n");
  3132.                       exit();
  3133.                       }
  3134.                    old_mode = fg_getmode();
  3135.                    fg_setmode(new_mode);
  3136.  
  3137.                    fg_setcolor(7);
  3138.                    fg_rect(0,159,0,99);
  3139.                    fg_setcolor(9);
  3140.                    fg_locate(6,6);
  3141.                    fg_text("quadrant",8);
  3142.                    fg_waitkey();
  3143.  
  3144. 166   Fastgraph User's Guide
  3145.  
  3146.                    fg_transfer(0,159,0,99,160, 99,0,0);
  3147.                    fg_transfer(0,319,0,99,  0,199,0,0);
  3148.                    fg_waitkey();
  3149.  
  3150.                    fg_setmode(old_mode);
  3151.                    fg_reset();
  3152.                 }
  3153.  
  3154.      The final routines pertaining to image transfer are fg_tcxfer and
  3155. fg_tcmask.  The fg_tcxfer routine is similar to fg_transfer in that it copies
  3156. a rectangular region from one position to another, but fg_tcxfer allows you
  3157. to treat one or more colors as transparent (the name fg_tcxfer stands for
  3158. "transparent color transfer").  In other words, any pixel whose color value
  3159. is defined to be transparent is not copied to the destination area.  The
  3160. fg_tcxfer routine's arguments are the same as for the fg_transfer routine,
  3161. but fg_tcxfer has no effect in text video modes.  Because fg_tcxfer must
  3162. examine the color of individual pixels, it is not as fast as the fg_transfer
  3163. routine, especially in the native EGA and VGA graphics video modes (modes 13
  3164. through 18).
  3165.  
  3166.      The fg_tcmask routine defines which colors are considered transparent in
  3167. subsequent calls to fg_tcxfer.  Its argument is an integer bit mask
  3168. (specifically, a 16-bit mask) where each bit indicates whether or not the
  3169. color is transparent.  For example, if bit 0 (the rightmost bit) is set in
  3170. the mask, then color 0 will be transparent; if bit 0 is reset, color 0 will
  3171. not be transparent.  Because the bit mask size is 16 bits, only the first 16
  3172. color values may be defined as transparent.
  3173.  
  3174.      Example 9-26 illustrates the use of the fg_tcxfer and fg_tcmask
  3175. routines.  This program is identical to example 9-25, except the color of the
  3176. word "quadrant" (color 9) is defined to be transparent, and fg_tcxfer is used
  3177. in place of fg_transfer.  Because color 9 maps to color 1 in the CGA four-
  3178. color graphics mode (mode 4), we must define both colors 1 and 9 to be
  3179. transparent (remember, fg_tcmask considers actual color values transparent,
  3180. not color indices).  The bit mask passed to fg_tcmask thus will be 0000 0010
  3181. 0000 0010 binary, or 0202 hex.  The end result of this program is the same as
  3182. example 9-25, but the word "quadrant" appears in the background color (color
  3183. 0) instead of color 9 in the upper right, lower left, and lower right
  3184. quadrants.
  3185.  
  3186.                                 Example 9-26.
  3187.  
  3188.                 main()
  3189.                 {
  3190.                    int new_mode, old_mode;
  3191.  
  3192.                    new_mode = fg_bestmode(320,200,1);
  3193.                    if (new_mode < 0 || new_mode == 12) {
  3194.                       printf("This program requires a 320 ");
  3195.                       printf("x 200 color graphics mode.\n");
  3196.                       exit();
  3197.                       }
  3198.                    old_mode = fg_getmode();
  3199.                    fg_setmode(new_mode);
  3200.  
  3201.                    fg_setcolor(7);
  3202.  
  3203.                                  Chapter 9:  Images and Image Management   167
  3204.  
  3205.                    fg_rect(0,159,0,99);
  3206.                    fg_setcolor(9);
  3207.                    fg_locate(6,6);
  3208.                    fg_text("quadrant",8);
  3209.                    fg_waitkey();
  3210.  
  3211.                    fg_tcmask(0x0202);
  3212.                    fg_tcxfer(0,159,0,99,160, 99,0,0);
  3213.                    fg_tcxfer(0,319,0,99,  0,199,0,0);
  3214.                    fg_waitkey();
  3215.  
  3216.                    fg_setmode(old_mode);
  3217.                    fg_reset();
  3218.                 }
  3219.  
  3220.  
  3221.  
  3222. Summary of Image Display Routines
  3223.  
  3224.      This section summarizes the functional descriptions of the Fastgraph
  3225. routines presented in this chapter.  More detailed information about these
  3226. routines, including their arguments and return values, may be found in the
  3227. Fastgraph Reference Manual.
  3228.  
  3229.      For all of the image display routines, images are positioned so that
  3230. their lower left corner is at the graphics cursor position (or text cursor
  3231. position for those routines that also work in text video modes).  For all of
  3232. the image transfer routines, Fastgraph extends the horizontal pixel
  3233. coordinates to a byte boundary when the routines are used in a graphics video
  3234. mode.
  3235.  
  3236.      FG_CLIPMASK displays a clipped image stored as a masking map.  This
  3237. routine has no effect when used in a text video mode.
  3238.  
  3239.      FG_CLPIMAGE displays a clipped image stored as a mode-specific bit map.
  3240. This routine has no effect when used in a text video mode.
  3241.  
  3242.      FG_DISPFILE displays an image stored in Fastgraph's standard or packed
  3243. pixel run format, where the image resides in an external file.  This routine
  3244. has no effect when used in a text video mode.
  3245.  
  3246.      FG_DISPLAY displays an image stored in Fastgraph's standard pixel run
  3247. format, where the image resides in an array.  This routine has no effect when
  3248. used in a text video mode.
  3249.  
  3250.      FG_DISPLAYP displays an image stored in Fastgraph's packed pixel run
  3251. format, where the image resides in an array.  This routine has no effect when
  3252. used in a text video mode.
  3253.  
  3254.      FG_DRAWMAP displays an image stored as a mode-independent bit map.  This
  3255. routine has no effect when used in a text video mode.
  3256.  
  3257.      FG_DRAWMASK displays an image stored as a masking map.  This routine has
  3258. no effect when used in a text video mode.
  3259.  
  3260.      FG_DRWIMAGE displays an image stored as a mode-specific bit map.
  3261. 168   Fastgraph User's Guide
  3262.  
  3263.      FG_FLIPMASK displays a reversed clipped image stored as a masking map.
  3264. This routine has no effect when used in a text video mode.
  3265.  
  3266.      FG_FLPIMAGE displays a reversed clipped image stored as a mode-specific
  3267. bit map.  This routine has no effect when used in a text video mode.
  3268.  
  3269.      FG_GETHPAGE returns the hidden page number, as defined in the most
  3270. recent call to fg_sethpage.
  3271.  
  3272.      FG_GETIMAGE retrieves an image as a mode-specific bit map.
  3273.  
  3274.      FG_GETMAP retrieves an image as a mode-independent bit map.  This
  3275. routine has no effect when used in a text video mode.
  3276.  
  3277.      FG_PATTERN defines a display pattern for use with the fg_dispfile,
  3278. fg_display, or fg_displayp routines.  This routine has no effect when used in
  3279. a text video mode.
  3280.  
  3281.      FG_RESTORE copies an image from the hidden video page to the same
  3282. position on the active video page.
  3283.  
  3284.      FG_RESTOREW is identical to fg_restore, but the image extremes are
  3285. specified as world space coordinates.
  3286.  
  3287.      FG_REVIMAGE displays a reversed image stored as a mode-specific bit map.
  3288. This routine has no effect when used in a text video mode.
  3289.  
  3290.      FG_REVMASK displays a reversed image stored as a masking map.  This
  3291. routine has no effect when used in a text video mode.
  3292.  
  3293.      FG_SAVE copies an image from the active video page to the same position
  3294. on the hidden video page.
  3295.  
  3296.      FG_SAVEW is identical to fg_save, but the image extremes are specified
  3297. as world space coordinates.
  3298.  
  3299.      FG_SETHPAGE defines the hidden video page (used by fg_restore,
  3300. fg_restorew, fg_save, and fg_savew).
  3301.  
  3302.      FG_TCMASK defines which colors the fg_tcxfer routine will consider
  3303. transparent.  This routine has no effect when used in a text video mode.
  3304.  
  3305.      FG_TCXFER copies an image from any position on any video page to any
  3306. position on any video page, excluding any pixels whose color value is
  3307. transparent.  This routine has no effect when used in a text video mode.
  3308.  
  3309.      FG_TRANSFER copies an image from any position on any video page to any
  3310. position on any video page.  It is Fastgraph's most general image transfer
  3311. routine.
  3312.  
  3313.  
  3314. Chapter 10
  3315.  
  3316. Animation Techniques
  3317. 170   Fastgraph User's Guide
  3318.  
  3319.  
  3320. Overview
  3321.  
  3322.      Unlike other microcomputers, the IBM PC and PS/2 family of systems do
  3323. not have any special graphics hardware or firmware to assist in performing
  3324. animation.  This means that any animation done on these systems must be
  3325. implemented entirely through software.  This chapter will describe how to do
  3326. this using Fastgraph's video page management and image management routines.
  3327. The methods described in this chapter are not intended to be all inclusive,
  3328. for that would itself fill a separate volume at least as large as this
  3329. manual.  However, the animation techniques presented here should provide a
  3330. basis that you can readily extend to develop more sophisticated uses of
  3331. animation.  The examples in this chapter are restricted to graphics video
  3332. modes.
  3333.  
  3334.  
  3335. Simple Animation
  3336.  
  3337.      The first type of animation we'll examine is called simple animation.
  3338. In simple animation, we display an object, erase it, and then display it in a
  3339. new position.  When we perform this "erase and redisplay" sequence
  3340. repetitively, the object moves.  This method, however, has two drawbacks.
  3341. First, unless the object is rather small, it will flicker because the erasing
  3342. and display of the object does not coincide with the refresh rate of the
  3343. video display.  Second, and perhaps more importantly, anything underneath the
  3344. object is not saved as the object moves across it.  Despite these
  3345. limitations, simple animation is sometimes useful, and it is a good place to
  3346. begin our discussion of animation techniques.
  3347.  
  3348.      Example 10-1 moves a small bright green rectangle (magenta in CGA) from
  3349. left to right across the screen in any 320 by 200 color graphics mode.  The
  3350. program moves the rectangle, which is 20 pixels wide and 10 pixels high,
  3351. using a for loop.  This loop first uses the fg_clprect routine to display the
  3352. rectangle, then uses the fg_waitfor routine to leave the object on the screen
  3353. momentarily, and finally uses fg_clprect again to erase the rectangle by
  3354. redisplaying it in the original background color (the fg_waitfor routine is
  3355. described in chapter 14).  We use fg_clprect rather than fg_rect because the
  3356. first few and last few loop iterations result in at least part of the
  3357. rectangle being off the screen.  Each successive loop iteration displays the
  3358. rectangle five pixels to the right of its previous position.
  3359.  
  3360.                                 Example 10-1.
  3361.  
  3362.           main()
  3363.           {
  3364.              int new_mode, old_mode;
  3365.              int x;
  3366.  
  3367.              /* initialize the video environment */
  3368.  
  3369.              new_mode = fg_bestmode(320,200,1);
  3370.              if (new_mode < 0 || new_mode == 12) {
  3371.                 printf("This program requires a 320 ");
  3372.                 printf("x 200 color graphics mode.\n");
  3373.                 exit();
  3374.                 }
  3375.  
  3376.                                        Chapter 10:  Animation Techniques   171
  3377.  
  3378.              old_mode = fg_getmode();
  3379.              fg_setmode(new_mode);
  3380.  
  3381.              /* move the object across the screen */
  3382.  
  3383.              for (x = -20; x < 320; x+=5) {
  3384.                 fg_setcolor(10);
  3385.                 fg_clprect(x,x+19,95,104);
  3386.                 fg_waitfor(1);
  3387.                 fg_setcolor(0);
  3388.                 fg_clprect(x,x+19,95,104);
  3389.                 }
  3390.  
  3391.              /* restore the original video mode and return to DOS */
  3392.  
  3393.              fg_setmode(old_mode);
  3394.              fg_reset();
  3395.           }
  3396.  
  3397.      Example 10-2 is identical to example 10-1, but it shows what happens
  3398. when we move the rectangle across an existing background (in this case, the
  3399. background is solid white).  If you run this program, you'll see that the
  3400. rectangle leaves a trail of color 0 behind it.  While this might be
  3401. occasionally useful, it demonstrates that simple animation is destructive
  3402. because it does not preserve the background.  In this particular example, if
  3403. we changed the second call to fg_setcolor within the for loop so revert to
  3404. color 15 instead of color 0, the background would be restored.  In general,
  3405. though, it may not be this easy to replace the background, so we must rely on
  3406. some other method for preserving it.
  3407.  
  3408.                                 Example 10-2.
  3409.  
  3410.           main()
  3411.           {
  3412.              int new_mode, old_mode;
  3413.              int x;
  3414.  
  3415.              /* initialize the video environment */
  3416.  
  3417.              new_mode = fg_bestmode(320,200,1);
  3418.              if (new_mode < 0 || new_mode == 12) {
  3419.                 printf("This program requires a 320 ");
  3420.                 printf("x 200 color graphics mode.\n");
  3421.                 exit();
  3422.                 }
  3423.              old_mode = fg_getmode();
  3424.              fg_setmode(new_mode);
  3425.  
  3426.              /* draw some type of background */
  3427.  
  3428.              fg_setcolor(15);
  3429.              fg_rect(0,319,0,199);
  3430.  
  3431.              /* move the object across the screen */
  3432.  
  3433.              for (x = -20; x < 320; x+=5) {
  3434.  
  3435. 172   Fastgraph User's Guide
  3436.  
  3437.                 fg_setcolor(10);
  3438.                 fg_clprect(x,x+19,95,104);
  3439.                 fg_waitfor(1);
  3440.                 fg_setcolor(0);
  3441.                 fg_clprect(x,x+19,95,104);
  3442.                 }
  3443.  
  3444.              /* restore the original video mode and return to DOS */
  3445.  
  3446.              fg_setmode(old_mode);
  3447.              fg_reset();
  3448.           }
  3449.  
  3450.      To summarize, we see that simple animation is easy to implement, but it
  3451. is destructive and typically causes the animated object to flicker.  For
  3452. these reasons, it is not used too frequently.
  3453.  
  3454.  
  3455. XOR Animation
  3456.  
  3457.      "Exclusive or" animation, or XOR animation for short, is an interesting
  3458. extension of simple animation and is most useful when animating a single-
  3459. color object against a single-color background.  Like simple animation, it
  3460. uses the "erase and redisplay" technique to move an object, but it does this
  3461. differently.  Instead of erasing the object by displaying it in the
  3462. background color, XOR animation does so by displaying it in the same color
  3463. using an exclusive or, or XOR, operation.  This method relies on a specific
  3464. property of the exclusive or operator:
  3465.  
  3466.                (object XOR background) XOR object = background
  3467.  
  3468. In other words, if you XOR something twice in the same position, the end
  3469. result is the same as the original image in that position.
  3470.  
  3471.      Example 10-3 demonstrates XOR animation.  This program is similar to
  3472. example 10-2, but it only runs in the 320 by 200 EGA graphics mode (mode 13).
  3473. After establishing the video mode, it uses the Fastgraph routine fg_setfunc
  3474. to select XOR mode.  This causes any subsequent graphics output to be XORed
  3475. with the contents of video memory instead of just replacing it.  The
  3476. fg_setfunc routine is described further in chapter 15.
  3477.  
  3478.      The other differences between examples 10-3 and 10-2 are that the call
  3479. to fg_setcolor has been moved outside the for loop, and that fg_setcolor
  3480. takes a different value.  Since the existing background is bright white
  3481. (color 15), we can't just use color 10 if we want to display a bright green
  3482. object.  The desired value is that which when XORed with color 15 produces
  3483. color 10; the easiest way to obtain this value is to XOR these two numbers.
  3484. The call to fg_setcolor can be moved outside the loop because we display the
  3485. object using the same color index throughout.
  3486.  
  3487.                                 Example 10-3.
  3488.  
  3489.           main()
  3490.           {
  3491.              int old_mode;
  3492.              int x;
  3493.  
  3494.                                        Chapter 10:  Animation Techniques   173
  3495.  
  3496.              /* initialize the video environment */
  3497.  
  3498.              if (fg_testmode(13,1) == 0) {
  3499.                 printf("This program requires EGA.\n");
  3500.                 exit();
  3501.                 }
  3502.              old_mode = fg_getmode();
  3503.              fg_setmode(13);
  3504.              fg_setfunc(3);
  3505.  
  3506.              /* draw some type of background */
  3507.  
  3508.              fg_setcolor(15);
  3509.              fg_rect(0,319,0,199);
  3510.  
  3511.              /* move the object across the screen */
  3512.  
  3513.              fg_setcolor(10^15);
  3514.              for (x = -20; x < 320; x+=5) {
  3515.                 fg_clprect(x,x+19,95,104);
  3516.                 fg_waitfor(1);
  3517.                 fg_clprect(x,x+19,95,104);
  3518.                 }
  3519.  
  3520.              /* restore the original video mode and return to DOS */
  3521.  
  3522.              fg_setmode(old_mode);
  3523.              fg_reset();
  3524.           }
  3525.  
  3526.  
  3527.      Fastgraph only supports the XOR pixel operation in the native EGA and
  3528. VGA graphics video modes (modes 13 through 18).  Thus, you cannot use XOR
  3529. animation in CGA, Tandy/PCjr, Hercules, or MCGA graphics modes.
  3530.  
  3531.      While XOR animation is non-destructive (that is, it restores the
  3532. original background), it still suffers from the flickering encountered in
  3533. simple animation.  In spite of this, it may be useful when animating a
  3534. single-color object against a single-color background.
  3535.  
  3536.  
  3537. Static Frame Animation
  3538.  
  3539.      Static frame animation uses a different strategy than simple animation
  3540. or XOR animation.  The general scheme of this method is to create the entire
  3541. animation sequence off-screen and then successively display each item, or
  3542. frame, in this sequence on one position of the visual video page.  This
  3543. results in a visually appealing animation which is non-destructive and does
  3544. not include the flickering associated with simple animation and XOR
  3545. animation.  Static frame animation requires the visual video page and one or
  3546. more additional pages to implement.  The number of pages needed depends on
  3547. the number of frames and the size of each frame.
  3548.  
  3549.      Example 10-4 runs in any 320 by 200 color graphics video mode and
  3550. illustrates a simple use of static frame animation.  The program displays an
  3551. animation sequence containing 12 frames; it displays this sequence three
  3552. times.  The animation sequence consists of a bright green rectangle (magenta
  3553. 174   Fastgraph User's Guide
  3554.  
  3555. in CGA) moving from left to right across the center of the frame.  Each frame
  3556. is 96 pixels wide and 50 pixels high.  The 12 frames are set up on an off-
  3557. screen video page as shown below.
  3558.  
  3559.                          0       95 96     191 192    287
  3560.  
  3561.                       0
  3562.                           frame 1    frame 2    frame 3
  3563.                      49
  3564.  
  3565.                      50
  3566.                           frame 4    frame 5    frame 6
  3567.                      99
  3568.  
  3569.                     100
  3570.                           frame 7    frame 8    frame 9
  3571.                     149
  3572.  
  3573.                     150
  3574.                           frame 10   frame 11   frame 12
  3575.                     199
  3576.  
  3577.      Example 10-4 first establishes the video mode and allocates the
  3578. additional video page (needed if using a video mode in which page 1 is a
  3579. virtual video page).  The program then generates the background for frame 1;
  3580. the background is a blue rectangle (cyan in CGA) with a white ellipse
  3581. centered on it.  After the call to fg_ellipse, the first frame is ready.
  3582.  
  3583.      The next step is to create the remaining 11 frames.  In frame 2, the
  3584. right half of the 20-pixel wide rectangle will enter the left edge of the
  3585. frame.  In frame 3, the rectangle will be ten pixels farther right, or
  3586. aligned against the left edge of the frame.  In frames 4 through 12, the
  3587. rectangle will be ten pixels farther right in each frame, so that by frame 12
  3588. only the left half of the rectangle appears on the right edge of the frame.
  3589. The first for loop in the program builds frames 2 through 12 by copying the
  3590. background from frame 1 and then displaying the rectangle (that is, the
  3591. animated object) in the proper position for that frame.
  3592.  
  3593.      The second for loop performs the animation sequence.  To display the 12-
  3594. frame sequence three times, it must perform 36 iterations.  The loop simply
  3595. copies each frame from the appropriate position on video page 1 to the middle
  3596. of the visual video page.  Note how the fg_waitfor routine is used to pause
  3597. momentarily between each frame.
  3598.  
  3599.                                 Example 10-4.
  3600.  
  3601.        #define VISUAL 0
  3602.        #define HIDDEN 1
  3603.  
  3604.        int xmin[] = {  0, 96,192,  0, 96,192,  0, 96,192,  0, 96,192};
  3605.        int ymax[] = { 49, 49, 49, 99, 99, 99,149,149,149,199,199,199};
  3606.  
  3607.        main()
  3608.        {
  3609.           int new_mode, old_mode;
  3610.           int frame, offset;
  3611.  
  3612.                                        Chapter 10:  Animation Techniques   175
  3613.  
  3614.           int i, x, y;
  3615.  
  3616.           /* initialize the video environment */
  3617.  
  3618.           new_mode = fg_bestmode(320,200,2);
  3619.           if (new_mode < 0 || new_mode == 12) {
  3620.              printf("This program requires a 320 ");
  3621.              printf("x 200 color graphics mode.\n");
  3622.              exit();
  3623.              }
  3624.           old_mode = fg_getmode();
  3625.           fg_setmode(new_mode);
  3626.           fg_allocate(HIDDEN);
  3627.  
  3628.           /* draw the background in the upper left corner */
  3629.  
  3630.           fg_setpage(HIDDEN);
  3631.           fg_setcolor(1);
  3632.           fg_rect(0,95,0,49);
  3633.           fg_setcolor(15);
  3634.           fg_move(48,25);
  3635.           fg_ellipse(20,20);
  3636.  
  3637.           /* display the animated object against each background */
  3638.  
  3639.           fg_setcolor(10);
  3640.           offset = -10;
  3641.           for (i = 1; i < 12; i++) {
  3642.              x = xmin[i];
  3643.              y = ymax[i];
  3644.              fg_transfer(0,95,0,49,x,y,HIDDEN,HIDDEN);
  3645.              fg_setclip(x,x+95,0,199);
  3646.              fg_clprect(x+offset,x+offset+19,y-29,y-20);
  3647.              offset += 10;
  3648.              }
  3649.  
  3650.           /* slide the object across the background three times */
  3651.  
  3652.           for (i = 0; i < 36; i++) {
  3653.              frame = i % 12;
  3654.              x = xmin[frame];
  3655.              y = ymax[frame];
  3656.              fg_transfer(x,x+95,y-49,y,112,124,HIDDEN,VISUAL);
  3657.              fg_waitfor(2);
  3658.              }
  3659.  
  3660.           /* restore the original video mode and return to DOS */
  3661.  
  3662.           fg_freepage(HIDDEN);
  3663.           fg_setmode(old_mode);
  3664.           fg_reset();
  3665.        }
  3666.  
  3667. 176   Fastgraph User's Guide
  3668.  
  3669.  
  3670. Dynamic Frame Animation
  3671.  
  3672.      Dynamic frame animation is similar to static frame animation, but the
  3673. all of the animation frames are built as needed during the animation sequence
  3674. instead of in advance.  When using this method, you must first store a copy
  3675. of the background on an off-screen video page.  Then, to build a frame,
  3676. create another copy (called the workspace) of the background elsewhere on the
  3677. off-screen page (or perhaps even to a different off-screen page) and display
  3678. the object on that copy.  Finally, transfer the workspace to the visual page.
  3679. Like static frame animation, this method produces a non-destructive, flicker-
  3680. free animation sequence.
  3681.  
  3682.      Example 10-5 is functionally identical to example 10-4, but it uses
  3683. dynamic rather than static frame animation.  As before, the program builds
  3684. the background in the upper left corner of video page 1, but it then uses
  3685. fg_transfer to copy it to the center of the visual video page.  The for loop
  3686. builds each frame as it is needed and likewise copies it to the center of the
  3687. visual page.  Again, fg_waitfor creates the necessary pause between frames.
  3688.  
  3689.                                 Example 10-5.
  3690.  
  3691.          #define VISUAL 0
  3692.          #define HIDDEN 1
  3693.  
  3694.          main()
  3695.          {
  3696.             int new_mode, old_mode;
  3697.             int frame, offset;
  3698.             int i;
  3699.  
  3700.             /* initialize the video environment */
  3701.  
  3702.             new_mode = fg_bestmode(320,200,2);
  3703.             if (new_mode < 0 || new_mode == 12) {
  3704.                printf("This program requires a 320 ");
  3705.                printf("x 200 color graphics mode.\n");
  3706.                exit();
  3707.                }
  3708.             old_mode = fg_getmode();
  3709.             fg_setmode(new_mode);
  3710.             fg_allocate(HIDDEN);
  3711.  
  3712.             /* draw the background in the upper left corner */
  3713.  
  3714.             fg_setpage(HIDDEN);
  3715.             fg_setcolor(1);
  3716.             fg_rect(0,95,0,49);
  3717.             fg_setcolor(15);
  3718.             fg_move(48,25);
  3719.             fg_ellipse(20,20);
  3720.  
  3721.             /* copy it to the center of the visual page */
  3722.  
  3723.             fg_transfer(0,95,0,49,112,124,HIDDEN,VISUAL);
  3724.  
  3725.  
  3726.                                        Chapter 10:  Animation Techniques   177
  3727.  
  3728.             /* slide the object across the background three times */
  3729.  
  3730.             fg_setcolor(10);
  3731.             for (i = 0; i < 36; i++) {
  3732.                frame  = i % 12;
  3733.                offset = 10 * frame - 10;
  3734.                fg_transfer(0,95,20,29,112,105,HIDDEN,HIDDEN);
  3735.                fg_rect(112+offset,131+offset,96,105);
  3736.                fg_transfer(112,207,96,105,112,105,HIDDEN,VISUAL);
  3737.                fg_waitfor(2);
  3738.                }
  3739.  
  3740.             /* restore the original video mode and return to DOS */
  3741.  
  3742.             fg_freepage(HIDDEN);
  3743.             fg_setmode(old_mode);
  3744.             fg_reset();
  3745.          }
  3746.  
  3747.  
  3748.      Two items in example 10-5 merit further discussion.  First, we have
  3749. chosen our workspace on page 1 so that it uses the same screen space
  3750. coordinates as the image area on the visual page.  This is not necessary
  3751. unless you are using the fg_restore routine instead of fg_transfer.  Second,
  3752. the program is able to use the faster fg_rect routine in place of fg_clprect.
  3753. It can do this because even though the object will extend beyond the
  3754. workspace limits, we only transfer the workspace itself.  However, for this
  3755. to function properly, the workspace's horizontal limits must fall on byte
  3756. boundaries.
  3757.  
  3758.      Note too that we do not need to transfer the entire frame during the
  3759. animation sequence.  In example 10-5, we know the vertical extremes of the
  3760. moving image are y=96 and y=105, so we only transfer 10 rows instead of the
  3761. entire frame.  We could similarly compute the x extremes for each frame and
  3762. only transfer the necessary portion.  Recall, however, that fg_transfer
  3763. extends the horizontal coordinates to byte boundaries, so we may copy a few
  3764. extra pixels as well.  This may or may not affect the animation sequence.
  3765. Again, the problem is eliminated if you align your workspace on byte
  3766. boundaries.
  3767.  
  3768.      When we use dynamic frame animation, it is fairly easy to change the
  3769. number of frames in the animation sequence.  Suppose we wish to produce a
  3770. smoother animation by increasing the number of frames from 12 to 24.  This
  3771. means the object will move in increments of five pixels instead of ten.  The
  3772. only changes needed are to double the number of loop iterations, modify the
  3773. calculations for the frame number and offset values as shown below, and
  3774. reduce the fg_waitfor pause from 2 to 1.
  3775.  
  3776.  
  3777.                            frame  = i % 24;
  3778.                            offset = 5 * frame - 10;
  3779.  
  3780.  
  3781. Compare this to all of the changes that would be necessary if we were using
  3782. static frame animation.
  3783. 178   Fastgraph User's Guide
  3784.  
  3785.  
  3786. Page Flipping
  3787.  
  3788.      Page flipping is a variation of frame animation in which you construct
  3789. images on off-screen video pages and then repetitively make those pages the
  3790. visual page.  We can further divide the page flipping technique into static
  3791. and dynamic variants, just as we did with frame animation.
  3792.  
  3793.      In static page flipping, we construct the entire animation sequence in
  3794. advance, with one frame per video page.  Once this is done, we can display
  3795. each frame by using the fg_setvpage routine to instantly switch from one
  3796. video page to another.  Although this produces a smooth, flicker-free
  3797. animation, we cannot carry the sequence very far before running out of video
  3798. pages (and hence animation frames).
  3799.  
  3800.      In dynamic page flipping, we construct each animation frame when it is
  3801. needed.  As in static page flipping, we construct each frame on a separate
  3802. video page.  However, as example 10-6 demonstrates, we only need three video
  3803. pages to produce the animation sequence, regardless of the number of frames
  3804. in the sequence.  Two of the three video pages will alternate as the visual
  3805. page, while the remaining video page keeps a copy of the background.
  3806.  
  3807.      Example 10-6, which performs an animation sequence similar to examples
  3808. 10-4 and 10-5, illustrates dynamic frame animation in the 320 by 200 EGA
  3809. graphics video mode (mode 13).  The program begins by displaying the
  3810. background on video page 2.  Video pages 0 and 1 will alternate as the visual
  3811. page; the page that is not the visual page is called the hidden page.  We
  3812. start with page 0 as the visual page, and hence page 1 as the hidden page.
  3813. To build each frame, the program uses fg_transfer to copy the background from
  3814. page 2 to the hidden page and then uses fg_clprect to display the animated
  3815. object at the correct position on the hidden page.  After this, it displays
  3816. the next frame by using fg_setvpage to make the hidden page the visual page.
  3817. Before beginning the next iteration, the program toggles the hidden page
  3818. number in preparation for the next frame.
  3819.  
  3820.                                 Example 10-6.
  3821.  
  3822.           main()
  3823.           {
  3824.              int old_mode;
  3825.              int hidden;
  3826.              int x;
  3827.  
  3828.              /* initialize the video environment */
  3829.  
  3830.              if (testmode(fg_13,3) == 0) {
  3831.                 printf("This program requires EGA.\n");
  3832.                 exit();
  3833.                 }
  3834.              old_mode = fg_getmode();
  3835.              fg_setmode(13);
  3836.  
  3837.              /* draw the background on page two */
  3838.  
  3839.              fg_setpage(2);
  3840.              fg_setcolor(1);
  3841.  
  3842.                                        Chapter 10:  Animation Techniques   179
  3843.  
  3844.              fg_rect(0,319,0,199);
  3845.              fg_setcolor(15);
  3846.              fg_move(160,100);
  3847.              fg_ellipse(20,20);
  3848.  
  3849.              /* slide the object across the screen */
  3850.  
  3851.              hidden = 1;
  3852.              setcolor(10);
  3853.              for (x = -10; x < 320; x+=4) {
  3854.                 fg_setpage(hidden);
  3855.                 fg_transfer(0,319,0,199,0,199,2,hidden);
  3856.                 fg_clprect(x,x+19,96,105);
  3857.                 fg_setvpage(hidden);
  3858.                 hidden = 1 - hidden;
  3859.                 fg_waitfor(1);
  3860.                 }
  3861.  
  3862.              /* restore the original video mode and return to DOS */
  3863.  
  3864.              fg_setmode(old_mode);
  3865.              fg_reset();
  3866.           }
  3867.  
  3868.      A problem with either page flipping technique arises if we use virtual
  3869. video pages.  Page flipping relies on the fact that changing the visual page
  3870. number occurs instantly, which is exactly what happens when we use physical
  3871. video pages.  However, such is not the case with virtual pages because
  3872. Fastgraph must copy the entire virtual page contents into video memory.
  3873. While this occurs quite rapidly, it is not instantaneous, and its effects are
  3874. immediately apparent on the animation.
  3875.  
  3876.  
  3877. Summary of Animation Techniques
  3878.  
  3879.      This chapter has presented five animation techniques:  simple animation,
  3880. XOR animation, static frame animation, dynamic frame animation, and page
  3881. flipping.  The following table summarizes their behavior.
  3882.  
  3883.                     technique  destructive?  flicker-free?
  3884.  
  3885.  
  3886.                     simple          yes           no
  3887.                     XOR             no            no
  3888.                     static frame    no            yes
  3889.                     dynamic frame   no            yes
  3890.                     page flipping   no            yes
  3891.  
  3892. Simple animation and XOR animation are elementary techniques that are seldom
  3893. used once you master the concepts of frame animation and page flipping.
  3894.  
  3895.      As stated at the beginning of this chapter, the simple examples
  3896. presented here serve as the basis for understanding the mechanics of the
  3897. animation techniques we have discussed.  In "real world" programs, you'll
  3898. typically want to display an image using the fg_drwimage or fg_drawmap
  3899. routines instead using rudimentary images such as the rectangles in our
  3900. 180   Fastgraph User's Guide
  3901.  
  3902. examples.  A helpful rule is to use pixel run maps (displayed by fg_dispfile,
  3903. fg_display, or fg_displayp) for both backgrounds and moving objects, and then
  3904. use fg_getimage or fg_getmap to retrieve the moving objects as bit-mapped
  3905. images for later display.  Of course, it is desirable to do all of this
  3906. "behind the scenes" work on video pages other than the visual page.
  3907.  
  3908.  
  3909. Chapter 11
  3910.  
  3911. Special Effects
  3912. 182   Fastgraph User's Guide
  3913.  
  3914.  
  3915. Overview
  3916.  
  3917.      This chapter will discuss the Fastgraph routines that help produce
  3918. special visual effects.  These include the ability to dissolve the screen
  3919. contents in small increments, scroll areas of the screen, and change the
  3920. physical origin of the screen.  The accompanying example programs illustrate
  3921. how to use these routines to produce some interesting effects.
  3922.  
  3923.  
  3924. Screen Dissolving
  3925.  
  3926.      Screen dissolving is the process of replacing the entire screen contents
  3927. in random small increments instead of all at once.  Fastgraph includes two
  3928. routines, fg_fadeout and fg_fadein, for this purpose.  The fg_fadeout routine
  3929. incrementally replaces the visual page contents with pixels of the current
  3930. color, while fg_fadein incrementally replaces the visual page contents with
  3931. the hidden page contents (that is, the page defined in the most recent call
  3932. to fg_sethpage).  Both routines accept an integer argument that defines the
  3933. delay between each incremental replacement.  A value of zero means to perform
  3934. the replacement as quickly as possible, while 1 is slightly slower, 2 is
  3935. slower yet, and so forth.  The fg_fadeout and fg_fadein routines have no
  3936. effect in text video modes.
  3937.  
  3938.      Example 11-1 shows how to use the fg_fadeout routine.  The program,
  3939. which runs in any graphics video mode, first fills the screen with a
  3940. rectangle of color 2.  After waiting for a keystroke, the program
  3941. incrementally replaces the screen contents with pixels of color 15 (the
  3942. current color index when fg_fadeout is called).  After another keystroke, the
  3943. program exits gracefully.
  3944.  
  3945.                                 Example 11-1.
  3946.  
  3947.                   main()
  3948.                   {
  3949.                      int old_mode;
  3950.  
  3951.                      old_mode = fg_getmode();
  3952.                      fg_setmode(fg_automode());
  3953.  
  3954.                      fg_setcolor(2);
  3955.                      fg_rect(0,fg_getmaxx(),0,fg_getmaxy());
  3956.                      fg_waitkey();
  3957.  
  3958.                      fg_setcolor(15);
  3959.                      fg_fadeout(0);
  3960.                      fg_waitkey();
  3961.  
  3962.                      fg_setmode(old_mode);
  3963.                      fg_reset();
  3964.                   }
  3965.  
  3966.  
  3967.      Example 11-2 shows how to use the fg_fadein routine in any 320 by 200
  3968. color graphics video mode.  The program first fills the screen with a
  3969. rectangle of color 2 and then fills video page 1 with a rectangle of color 1.
  3970. After waiting for a keystroke, the program incrementally transfers the
  3971.                                             Chapter 11:  Special Effects   183
  3972.  
  3973. contents of page 1 to the visual page.  After the call to fg_fadein, both
  3974. page 0 (the visual page) and page 1 (the hidden page) will contain rectangles
  3975. of color 1 that fill the entire video page.  Finally, the program waits for
  3976. another keystroke before returning to DOS.
  3977.  
  3978.                                 Example 11-2.
  3979.  
  3980.                 main()
  3981.                 {
  3982.                    int new_mode, old_mode;
  3983.  
  3984.                    new_mode = fg_bestmode(320,200,2);
  3985.                    if (new_mode < 0 || new_mode == 12) {
  3986.                       printf("This program requires a 320 ");
  3987.                       printf("x 200 color graphics mode.\n");
  3988.                       exit();
  3989.                       }
  3990.                    old_mode = fg_getmode();
  3991.                    fg_setmode(new_mode);
  3992.                    fg_allocate(1);
  3993.                    fg_sethpage(1);
  3994.  
  3995.                    fg_setcolor(2);
  3996.                    fg_rect(0,319,0,199);
  3997.                    fg_setpage(1);
  3998.                    fg_setcolor(1);
  3999.                    fg_rect(0,319,0,199);
  4000.                    fg_waitkey();
  4001.  
  4002.                    fg_fadein(0);
  4003.                    fg_waitkey();
  4004.  
  4005.                    fg_freepage(1);
  4006.                    fg_setmode(old_mode);
  4007.                    fg_reset();
  4008.                 }
  4009.  
  4010.  
  4011.      You can also produce some appealing visual effects by replacing the
  4012. screen contents in a non-random fashion using the fg_restore or fg_transfer
  4013. routines.  For example, you could copy the hidden page contents to the visual
  4014. page with a through a series of concentric rectangular areas, each slightly
  4015. larger than the previous, until the entire screen is copied.  Another
  4016. interesting effect is to start around the screen perimeter and proceed toward
  4017. the screen center, thus producing a "snake-like" effect.  Experimenting with
  4018. such techniques may reveal other effects that suit your application.
  4019.  
  4020.  
  4021. Scrolling
  4022.  
  4023.      Another useful effect is scrolling, and Fastgraph provides a routine
  4024. that performs vertical scrolling within a given region of the active video
  4025. page.  The fg_scroll routine scrolls a region defined in screen space or
  4026. character space.  It can scroll up or down and offers two types of scrolling:
  4027. circular and end-off.  In circular scrolling, rows that scroll off one edge
  4028. of the defined region appear at its opposite edge.  In end-off scrolling,
  4029. 184   Fastgraph User's Guide
  4030.  
  4031. such rows are simply wind up above or below the scrolling area.  The
  4032. following diagrams illustrate the two types of scrolling.
  4033.  
  4034.                 end-off scrolling            circular scrolling
  4035.  
  4036.               before         after          before         after
  4037.  
  4038.                                C                             B
  4039.                  A                             A
  4040.  
  4041.                                A                             A
  4042.                  B                             B
  4043.  
  4044.      In these diagrams, the area bounded by the double lines is the scrolling
  4045. region, as specified in the call to fg_scroll.  Also, the scrolling direction
  4046. is assumed to be down (that is, toward the bottom of the screen), and the
  4047. number of rows to scroll is the height of the area designated B.  The number
  4048. of rows to scroll is often called the scrolling increment.
  4049.  
  4050.      For the end-off scrolling example, the scrolling operation transfers
  4051. region A downward so that part of it is copied into area B.  The area C
  4052. (which is the same size as area B) at the top of the scrolling region is
  4053. filled with pixels of the current color index (as defined in the most recent
  4054. call to fg_setcolor), and the original contents of area B are lost.  The
  4055. circular scrolling example also copies region A downward into the original
  4056. area B.  Unlike end-off scrolling, however, circular scrolling preserves the
  4057. area B by copying it to the opposite edge of the scrolling region.
  4058.  
  4059.      The fg_scroll routine takes six arguments.  The first four define the
  4060. scrolling region in the order minimum x coordinate, maximum x coordinate,
  4061. minimum y coordinate, and maximum y coordinate.  In graphics video modes, the
  4062. x coordinates are extended by byte boundaries (see page 158) if needed.  The
  4063. fifth argument is the scrolling increment.  It specifies the number of rows
  4064. to scroll.  If it is positive, the scrolling direction is toward the bottom
  4065. of the screen; if it is negative, the scrolling direction is toward the top
  4066. of the screen.  The sixth and final argument specifies the scroll type.  If
  4067. this value is zero, the scroll will be circular; if it is any other value,
  4068. the scroll will be end-off.  If the scroll type is circular, Fastgraph will
  4069. use the hidden page (as defined in the most recent call to fg_sethpage) as a
  4070. workspace (more specifically, the area bounded by the scrolling region
  4071. extremes on the hidden page will be used).  We'll now present three example
  4072. programs that use the fg_scroll routine.
  4073.  
  4074.      Example 11-3 runs in any 320 by 200 graphics video mode.  The program
  4075. displays two lines of text ("line one" and "line two") in the upper left
  4076. corner of the screen against a white background.  It then uses the fg_scroll
  4077. routine to move the second line down four pixel rows using an end-off scroll.
  4078. After waiting for a keystroke, the program again uses fg_scroll to move the
  4079. text back to its original position.  Note especially how the fg_setcolor
  4080. routine appears before the first call to fg_scroll to replace the "scrolled
  4081. off" rows with pixels of color 15, thus preserving the white background.
  4082.  
  4083.                                 Example 11-3.
  4084.  
  4085.                 main()
  4086.                 {
  4087.  
  4088.                                             Chapter 11:  Special Effects   185
  4089.  
  4090.                    int new_mode, old_mode;
  4091.  
  4092.                    new_mode = fg_bestmode(320,200,1);
  4093.                    if (new_mode < 0 || new_mode == 12) {
  4094.                       printf("This program requires a 320 ");
  4095.                       printf("x 200 color graphics mode.\n");
  4096.                       exit();
  4097.                       }
  4098.                    old_mode = fg_getmode();
  4099.                    fg_setmode(new_mode);
  4100.  
  4101.                    fg_setcolor(15);
  4102.                    fg_rect(0,319,0,199);
  4103.                    fg_setcolor(10);
  4104.                    fg_text("line one",8);
  4105.                    fg_locate(1,0);
  4106.                    fg_text("line two",8);
  4107.                    fg_waitkey();
  4108.  
  4109.                    fg_setcolor(15);
  4110.                    fg_scroll(0,63,8,15,4,1);
  4111.                    fg_waitkey();
  4112.                    fg_scroll(0,63,12,19,-4,1);
  4113.                    fg_waitkey();
  4114.  
  4115.                    fg_setmode(old_mode);
  4116.                    fg_reset();
  4117.                 }
  4118.  
  4119.      Example 11-4 is similar to example 11-3, but it runs in the 80-column
  4120. color text mode (mode 3).  In text modes, we cannot scroll half a character
  4121. row (four pixels) as in example 11-3, so the program scrolls the minimum one
  4122. row instead.
  4123.  
  4124.                                 Example 11-4.
  4125.  
  4126.                          main()
  4127.                          {
  4128.                             int old_mode;
  4129.  
  4130.                             old_mode = fg_getmode();
  4131.                             fg_setmode(3);
  4132.                             fg_cursor(0);
  4133.  
  4134.                             fg_setcolor(7);
  4135.                             fg_rect(0,79,0,24);
  4136.                             fg_setattr(10,7,0);
  4137.                             fg_text("line one",8);
  4138.                             fg_locate(1,0);
  4139.                             fg_text("line two",8);
  4140.                             fg_waitkey();
  4141.  
  4142.                             fg_setcolor(7);
  4143.                             fg_scroll(0,7,1,1,1,1);
  4144.                             fg_waitkey();
  4145.                             fg_scroll(0,7,2,2,-1,1);
  4146.  
  4147. 186   Fastgraph User's Guide
  4148.  
  4149.                             fg_waitkey();
  4150.  
  4151.                             fg_setmode(old_mode);
  4152.                             fg_reset();
  4153.                          }
  4154.  
  4155.  
  4156.      Example 11-5, the final scrolling example, demonstrates a circular
  4157. scroll.  The program runs in any 320 by 200 color graphics video mode; note
  4158. the use of video page 1 for the workspace required when the fg_scroll routine
  4159. performs a circular scroll.  The program first fills the screen with a light
  4160. blue rectangle (cyan in CGA), displays a smaller white rectangle in the
  4161. center of the screen, and then uses fg_move, fg_draw, and fg_paint to display
  4162. a light green star (magenta in CGA) within the white rectangle.  The program
  4163. executes a while loop to scroll the star upward in four pixel increments.
  4164. Because the scroll is circular, rows of the star that "scroll off" the top
  4165. edge of the white rectangle (whose height is the same as the scrolling
  4166. region) reappear at its bottom edge.  The use of fg_waitfor within the loop
  4167. simply slows down the scroll.  The scrolling continues until any key is
  4168. pressed.
  4169.  
  4170.                                 Example 11-5.
  4171.  
  4172.                 main()
  4173.                 {
  4174.                    int new_mode, old_mode;
  4175.  
  4176.                    new_mode = fg_bestmode(320,200,2);
  4177.                    if (new_mode < 0 || new_mode == 12) {
  4178.                       printf("This program requires a 320 ");
  4179.                       printf("x 200 color graphics mode.\n");
  4180.                       exit();
  4181.                       }
  4182.                    old_mode = fg_getmode();
  4183.                    fg_setmode(new_mode);
  4184.                    fg_allocate(1);
  4185.                    fg_sethpage(1);
  4186.  
  4187.                    fg_setcolor(9);
  4188.                    fg_rect(0,319,0,199);
  4189.                    fg_setcolor(15);
  4190.                    fg_rect(132,188,50,150);
  4191.  
  4192.                    fg_setcolor(10);
  4193.                    fg_move(160,67);
  4194.                    fg_draw(175,107);
  4195.                    fg_draw(140,82);
  4196.                    fg_draw(180,82);
  4197.                    fg_draw(145,107);
  4198.                    fg_draw(160,67);
  4199.                    fg_paint(160,77);
  4200.                    fg_paint(150,87);
  4201.                    fg_paint(160,87);
  4202.                    fg_paint(170,87);
  4203.                    fg_paint(155,97);
  4204.                    fg_paint(165,97);
  4205.  
  4206.                                             Chapter 11:  Special Effects   187
  4207.  
  4208.                    while (kbhit() == 0) {
  4209.                       fg_waitfor(1);
  4210.                       fg_scroll(136,184,50,150,-4,0);
  4211.                       }
  4212.                    fg_waitkey();
  4213.  
  4214.                    fg_freepage(1);
  4215.                    fg_setmode(old_mode);
  4216.                    fg_reset();
  4217.                 }
  4218.  
  4219.  
  4220.  
  4221. Changing the Screen Origin
  4222.  
  4223.      Fastgraph includes two routines for changing the screen origin.  By
  4224. changing the screen origin, we simply mean defining the (x,y) coordinate of
  4225. the upper left corner of the display area.  The fg_pan routine performs this
  4226. function in screen space, while the fg_panw routine does in world space.
  4227. Neither routine changes the graphics cursor position.
  4228.  
  4229.      Each of these routines has two arguments that specify the x and y
  4230. coordinates of the screen origin.  For the fg_pan routine, the arguments are
  4231. integer quantities.  For the fg_panw routine, they are floating point
  4232. quantities.
  4233.  
  4234.      In the native EGA and VGA graphics modes (modes 13 through 18), you can
  4235. set the screen origin to any (x,y) coordinate position (that is, to any
  4236. pixel).  In the other graphics modes, certain restrictions exist, as imposed
  4237. by specific video hardware.  These constraints limit the coordinate positions
  4238. that can be used as the screen origin.  Fastgraph compensates for these
  4239. restrictions by reducing the specified x and y coordinates to values that are
  4240. acceptable to the current video mode, as shown in the following table.
  4241.  
  4242.  
  4243.                            x will be reduced  y will be reduced
  4244.                video mode  to a multiple of:  to a multiple of:
  4245.  
  4246.                   4, 5             8                  2
  4247.                     6             16                  2
  4248.                     9              4                  4
  4249.                    11              8                  4
  4250.                    12              4                2 or 3
  4251.                19, 20, 21          4                  1
  4252.  
  4253.  
  4254. For example, in modes 4 and 5, the x coordinate will be reduced to a multiple
  4255. of 8 pixels, and the y coordinate will be reduced to a multiple of 2 pixels.
  4256. In the Hercules low resolution mode (mode 12), the y coordinate reduction
  4257. depends on whether or not the specified pixel row is scan doubled.
  4258.  
  4259.      Example 11-6 shows a useful effect that can be made with the fg_pan or
  4260. fg_panw routines.  This program uses the fg_automode routine to select a
  4261. video mode and then draws an unfilled rectangle in color 15 (bright white).
  4262. The top and bottom sides of the rectangle are intentionally drawn just
  4263. smaller than the physical screen size.  After waiting for a keystroke, the
  4264. 188   Fastgraph User's Guide
  4265.  
  4266. program uses a for loop to make the rectangle jiggle up and down.  The
  4267. rectangle moves because the fg_pan routine is called inside the loop to
  4268. switch the screen origin between the rectangle's upper left corner and the
  4269. original origin.  Note also the use of the fg_waitfor routine to cause slight
  4270. delays after each call to fg_pan.  If we didn't use fg_waitfor, the changing
  4271. of the origin would occur so rapidly we wouldn't notice the effect.  Finally,
  4272. the program restores the original video mode and screen attributes before
  4273. returning to DOS.
  4274.  
  4275.                                 Example 11-6.
  4276.  
  4277.                  #define DELAY 2
  4278.                  #define JUMP  4
  4279.  
  4280.                  main()
  4281.                  {
  4282.                     int i;
  4283.                     int old_mode;
  4284.  
  4285.                     old_mode = fg_getmode();
  4286.                     fg_setmode(fg_automode());
  4287.  
  4288.                     fg_setcolor(15);
  4289.                     fg_move(0,JUMP);
  4290.                     fg_draw(fg_getmaxx(),JUMP);
  4291.                     fg_draw(fg_getmaxx(),fg_getmaxy()-JUMP);
  4292.                     fg_draw(0,fg_getmaxy()-JUMP);
  4293.                     fg_draw(0,JUMP);
  4294.                     fg_waitkey();
  4295.  
  4296.                     for (i = 0; i < 6; i++) {
  4297.                        fg_pan(0,JUMP);
  4298.                        fg_waitfor(DELAY);
  4299.                        fg_pan(0,0);
  4300.                        fg_waitfor(DELAY);
  4301.                        }
  4302.  
  4303.                     fg_setmode(old_mode);
  4304.                     fg_reset();
  4305.                  }
  4306.  
  4307.  
  4308.  
  4309. Summary of Special Effects Routines
  4310.  
  4311.      This section summarizes the functional descriptions of the Fastgraph
  4312. routines presented in this chapter.  More detailed information about these
  4313. routines, including their arguments and return values, may be found in the
  4314. Fastgraph Reference Manual.
  4315.  
  4316.      FG_FADEIN incrementally replaces the visual page contents with the
  4317. hidden page contents.  This routine has no effect in text video modes.
  4318.  
  4319.      FG_FADEOUT incrementally replaces the visual page contents with pixels
  4320. of the current color.  This routine has no effect in text video modes.
  4321.                                             Chapter 11:  Special Effects   189
  4322.  
  4323.      FG_PAN changes the screen origin (the upper left corner of the screen)
  4324. to the specified screen space coordinates.  This routine has no effect in
  4325. text video modes.
  4326.  
  4327.      FG_PANW is the world space version of the fg_pan routine.
  4328.  
  4329.      FG_SCROLL vertically scrolls a region of the active video page.  The
  4330. scrolling may be done either up or down, using either an end-off or circular
  4331. method.  Circular scrolling uses part of the hidden page as a temporary
  4332. workspace.
  4333. 190   Fastgraph User's Guide
  4334.  
  4335.  
  4336. Chapter 12
  4337.  
  4338. Input Device Support
  4339. 192   Fastgraph User's Guide
  4340.  
  4341.  
  4342. Overview
  4343.  
  4344.      The selection of application input devices is an important part of
  4345. designing a program for the IBM PC and PS/2 family of systems.  The keyboard
  4346. and mouse are very popular, and in fact more and more applications,
  4347. especially those that utilize a graphical interface, actually require a mouse
  4348. to use the product.  Another input device, primarily used in entertainment
  4349. software, is the joystick.  Although not as popular as the mouse, the
  4350. joystick nevertheless can simplify the use of certain applications.
  4351. Fastgraph provides support for these three types of input devices, and this
  4352. chapter will discuss this in detail.
  4353.  
  4354.  
  4355. Keyboard Support
  4356.  
  4357.      Fastgraph's keyboard support includes routines to read keystrokes, check
  4358. the state of certain keys, and set the state of these keys.  These routines
  4359. are independent of the other parts of Fastgraph and thus do not require that
  4360. you call fg_setmode.  All of the keyboard-related routines work in text and
  4361. graphics video modes.
  4362.  
  4363.      The IBM PC and PS/2 keyboards produce two types of character codes --
  4364. standard codes and extended codes (extended codes are sometimes called
  4365. auxiliary codes).  The standard codes correspond to the 128 characters in the
  4366. ASCII character set.  In general, pressing keys on the main part of the
  4367. keyboard, or on the numeric keypad with NumLock turned on, will generate a
  4368. standard code.  The 128 extended codes are specific to the IBM PC and PS/2
  4369. keyboards.  Some common keystrokes that produce extended codes are keys on
  4370. the numeric keypad with NumLock turned off, the function keys, or pressing
  4371. Alt with another key.  The following tables show the all of the standard and
  4372. extended keyboard codes.
  4373.                                        Chapter 12:  Input Device Support   193
  4374.  
  4375.                        Table of standard keyboard codes
  4376.  
  4377.           key     code   key     code   key     code   key     code
  4378.  
  4379.           (none)    0    space    32    @        64    `        96
  4380.           Ctrl+A    1    !        33    A        65    a        97
  4381.           Ctrl+B    2    "        34    B        66    b        98
  4382.           Ctrl+C    3    #        35    C        67    c        99
  4383.           Ctrl+D    4    $        36    D        68    d       100
  4384.           Ctrl+E    5    %        37    E        69    e       101
  4385.           Ctrl+F    6    &        38    F        70    f       102
  4386.           Ctrl+G    7    '        39    G        71    g       103
  4387.           Ctrl+H    8    (        40    H        72    h       104
  4388.           Ctrl+I    9    )        41    I        73    i       105
  4389.           Ctrl+J   10    *        42    J        74    j       106
  4390.           Ctrl+K   11    +        43    K        75    k       107
  4391.           Ctrl+L   12    ,        44    L        76    l       108
  4392.           Ctrl+M   13    -        45    M        77    m       109
  4393.           Ctrl+N   14    .        46    N        78    n       110
  4394.           Ctrl+O   15    /        47    O        79    o       111
  4395.           Ctrl+P   16    0        48    P        80    p       112
  4396.           Ctrl+Q   17    1        49    Q        81    q       113
  4397.           Ctrl+R   18    2        50    R        82    r       114
  4398.           Ctrl+S   19    3        51    S        83    s       115
  4399.           Ctrl+T   20    4        52    T        84    t       116
  4400.           Ctrl+U   21    5        53    U        85    u       117
  4401.           Ctrl+V   22    6        54    V        86    v       118
  4402.           Ctrl+W   23    7        55    W        87    w       119
  4403.           Ctrl+X   24    8        56    X        88    x       120
  4404.           Ctrl+Y   25    9        57    Y        89    y       121
  4405.           Ctrl+Z   26    :        58    Z        90    z       122
  4406.           Ctrl+[   27    ;        59    [        91    {       123
  4407.           Ctrl+\   28    <        60    \        92    |       124
  4408.           Ctrl+]   29    =        61    ]        93    }       125
  4409.           Ctrl+^   30    >        62    ^        94    ~       126
  4410.           Ctrl+-   31    ?        63    _        95    Ctrl+BS 127
  4411.  
  4412. 194   Fastgraph User's Guide
  4413.  
  4414.                        Table of extended keyboard codes
  4415.  
  4416.               code        key
  4417.  
  4418.                3          Ctrl+@
  4419.               15          Shift+Tab (back tab)
  4420.               16-25       Alt+Q to Alt+P (top row of letters)
  4421.               30-38       Alt+A to Alt+L (middle row of letters)
  4422.               44-50       Alt+Z to Alt+M (bottom row of letters)
  4423.               59-68       F1 to F10
  4424.               71          Home
  4425.               72          up arrow
  4426.               73          PgUp
  4427.               75          left arrow
  4428.               77          right arrow
  4429.               79          End
  4430.               80          down arrow
  4431.               81          PgDn
  4432.               82          Ins
  4433.               83          Del
  4434.               84-93       Shift+F1 to Shift+F10
  4435.               94-103      Ctrl+F1 to Ctrl+F10
  4436.               104-113     Alt+F1 to Alt+F10
  4437.               114         Ctrl+PrtSc
  4438.               115         Ctrl+left arrow
  4439.               116         Ctrl+right arrow
  4440.               117         Ctrl+End
  4441.               118         Ctrl+PgDn
  4442.               119         Ctrl+Home
  4443.               120-131     Alt+1 to Alt+= (top row of keys)
  4444.               132         Ctrl+PgUp
  4445.  
  4446.  
  4447. In addition, four keys generate the same standard codes as other control key
  4448. combinations.  These keys are:
  4449.  
  4450.  
  4451.                          key        same as  code
  4452.  
  4453.                          Backspace  Ctrl+H   8
  4454.                          Tab        Ctrl+I   9
  4455.                          Enter      Ctrl+M   13
  4456.                          Escape     Ctrl+[   27
  4457.  
  4458.  
  4459.      The CapsLock, NumLock, and ScrollLock keys do not generate a standard or
  4460. extended code when pressed.  Instead, they toggle between off and on states.
  4461.  
  4462.  
  4463. Reading Keystrokes
  4464.  
  4465.      When you press a key or key combination, the standard or extended code
  4466. representing that keystroke is stored in the ROM BIOS keyboard buffer.  This
  4467. buffer can hold up to 16 keystrokes and thus provides a type-ahead
  4468. capability.  Fastgraph includes three routines for reading keystroke
  4469. information from the keyboard buffer.  The fg_getkey routine reads the next
  4470. item in the keyboard buffer if one is available (that is, if a key has been
  4471.                                        Chapter 12:  Input Device Support   195
  4472.  
  4473. pressed).  If the keyboard buffer is empty (meaning no key has been pressed),
  4474. fg_getkey waits for a keystroke and then reports information about it.
  4475. Another routine, fg_intkey, reads the next keystroke from the keyboard buffer
  4476. if one is available.  If the keyboard buffer is empty, fg_intkey immediately
  4477. returns and reports this condition.  The fg_intkey routine is useful when a
  4478. program must continue performing a task until a key is pressed.  We've
  4479. already seen the third routine, fg_waitkey, which flushes the keyboard buffer
  4480. and then waits for another keystroke.  Unlike fg_getkey and fg_intkey,
  4481. fg_waitkey does not return any keystroke information.  It is most useful in
  4482. "press any key to continue" situations.
  4483.  
  4484.      Both the fg_getkey and fg_intkey routines require two one-byte arguments
  4485. passed by reference.  If the keystroke is represented by a standard keyboard
  4486. code, fg_getkey and fg_intkey return its code in the first argument and set
  4487. the second argument to zero.  Similarly, if the keystroke generates an
  4488. extended code, the routines return its code in the second argument and set
  4489. the first argument to zero.  If the fg_intkey routine detects an empty
  4490. keyboard buffer, it sets both arguments to zero.
  4491.  
  4492.      Example 12-1 is a simple program that uses the fg_getkey routine.  It
  4493. solicits keystrokes and then displays the two values returned by fg_getkey,
  4494. one of which will always be zero.  The variable key receives the key's
  4495. standard code, while aux receives its extended code.  Note that fg_getkey is
  4496. the only Fastgraph routine in the program; this can be done because the
  4497. keyboard support routines are logically independent from the rest of
  4498. Fastgraph.  The program returns to DOS when you press the Escape key.
  4499.  
  4500.                                 Example 12-1.
  4501.  
  4502.                #define ESC 27
  4503.  
  4504.                main()
  4505.                {
  4506.                   unsigned char key, aux;
  4507.  
  4508.                   fg_getkey(&key,&aux);
  4509.                   while (key != ESC) {
  4510.                      printf("key = %3d  aux = %3d\n",key,aux);
  4511.                      fg_getkey(&key,&aux);
  4512.                      }
  4513.                }
  4514.  
  4515.      Example 12-2 reads keystrokes using the fg_intkey routine at half-second
  4516. intervals (18 fg_waitfor units equals one second).  As in the previous
  4517. example, the program displays the standard and extended codes for each
  4518. keystroke.  However, example 12-2 will continuously execute the while loop
  4519. even if no keystrokes are available, in which case the key and aux values
  4520. will both be zero.  The program returns to DOS when you press the Escape key.
  4521.  
  4522.                                 Example 12-2.
  4523.  
  4524.                #define ESC 27
  4525.  
  4526.                main()
  4527.                {
  4528.                   unsigned char key, aux;
  4529.  
  4530. 196   Fastgraph User's Guide
  4531.  
  4532.  
  4533.                   fg_intkey(&key,&aux);
  4534.                   while (key != ESC) {
  4535.                      printf("key = %3d  aux = %3d\n",key,aux);
  4536.                      fg_waitfor(9);
  4537.                      fg_intkey(&key,&aux);
  4538.                      }
  4539.                }
  4540.  
  4541.  
  4542.      When you use fg_intkey in a "tight" loop that does little else, you
  4543. should force a small delay within the loop by calling fg_waitfor as in
  4544. example 12-2.  Typically a delay of one or two clock ticks is sufficient.
  4545. Without this delay, the BIOS may not be able to process all keyboard
  4546. activity, and thus some keystrokes may not be available to your program.
  4547.  
  4548.  
  4549. Testing and Setting Key States
  4550.  
  4551.      As mentioned earlier, the CapsLock, NumLock, and ScrollLock keys do not
  4552. generate a standard or extended code when pressed but instead toggle between
  4553. off and on states.  Fastgraph includes routines for checking the state of
  4554. these keys, as well as setting the state of the CapsLock and NumLock keys.
  4555.  
  4556.      The Fastgraph routines fg_capslock, fg_numlock, and fg_scrlock
  4557. respectively read the state of the CapsLock, NumLock, and ScrollLock keys.
  4558. Each routine has no arguments and returns the key state as its function
  4559. value.  A return value of 0 means the associated key is in the off state,
  4560. while 1 indicates the key is in the on state.  If the keyboard does not have
  4561. a ScrollLock key, fg_scrlock considers the key off and returns a value of
  4562. zero.
  4563.  
  4564.      Example 12-3 is a simple program that uses the fg_capslock, fg_numlock,
  4565. and fg_scrlock routines to print messages describing the current state of
  4566. these three keys.
  4567.  
  4568.                                 Example 12-3.
  4569.  
  4570.                     main()
  4571.                     {
  4572.                        if (fg_capslock())
  4573.                           printf("CapsLock is on.\n");
  4574.                        else
  4575.                           printf("CapsLock is off.\n");
  4576.  
  4577.                        if (fg_numlock())
  4578.                           printf("NumLock is on.\n");
  4579.                        else
  4580.                           printf("NumLock is off.\n");
  4581.  
  4582.                        if (fg_scrlock())
  4583.                           printf("ScrollLock is on.\n");
  4584.                        else
  4585.                           printf("ScrollLock is off.\n");
  4586.                     }
  4587.  
  4588.                                        Chapter 12:  Input Device Support   197
  4589.  
  4590.      You can also set the state of the CapsLock and NumLock keys within a
  4591. program.  Fastgraph includes two routines, fg_setcaps and fg_setnum, for this
  4592. purpose.  Each routine requires an integer argument that specifies the new
  4593. key state.  If the argument value is 0, the key will be turned off; if the
  4594. value is 1, the key will be turned on.  Example 12-4 uses fg_setcaps and
  4595. fg_setnum to turn off CapsLock and NumLock.
  4596.  
  4597.                                 Example 12-4.
  4598.  
  4599.                               main()
  4600.                               {
  4601.                                  fg_setcaps(0);
  4602.                                  fg_setnum(0);
  4603.                               }
  4604.  
  4605.  
  4606.      On most keyboards, changing key states with fg_setcaps or fg_setnum will
  4607. also change the keyboard state light to reflect the new key state.  However,
  4608. some older keyboards, especially when used on PC, PC/XT, or Tandy 1000
  4609. systems, do not update the state light.  This makes the state light
  4610. inconsistent with the true key state.
  4611.  
  4612.  
  4613. Mouse Support
  4614.  
  4615.      The mouse is a very popular input and pointing device, especially in
  4616. graphically-oriented programs.  Fastgraph contains a number of routines to
  4617. support mice.  These routines perform such tasks as mouse initialization,
  4618. controlling and defining the mouse cursor, and reporting information about
  4619. the mouse position and button status.
  4620.  
  4621.      The underlying software that controls the mouse is called the mouse
  4622. driver.  Fastgraph's mouse support routines provide a high-level interface to
  4623. this driver.  The Microsoft Mouse and its accompanying mouse driver have
  4624. essentially become an industry standard, and other manufacturers of mice have
  4625. also made their mouse drivers Microsoft compatible.  For this reason, the
  4626. Fastgraph mouse support routines assume you are using a Microsoft or
  4627. compatible mouse driver.
  4628.  
  4629.      Unfortunately, not all mouse drivers are created equal.  That is, some
  4630. drivers are not truly Microsoft compatible, even though they may be
  4631. advertised as such.  In some cases, these incompatibilities are rather
  4632. trivial, but others are significant.  For example, early versions of some
  4633. third party mouse drivers had real problems in the EGA graphics modes.  The
  4634. Microsoft mouse driver, the Logitech mouse driver (version 3.2 or above), and
  4635. the DFI mouse driver (version 3.00 or above) are known to work well with
  4636. Fastgraph's mouse support routines.  Any other Microsoft compatible mouse
  4637. driver should also work properly.
  4638.  
  4639.  
  4640. Initializing the Mouse
  4641.  
  4642.      There are two steps required to use Fastgraph's mouse support routines
  4643. within an application program.  First, you must install the mouse driver.
  4644. This is done prior to running the application, typically by entering the
  4645. command MOUSE at the DOS command prompt.  Second, you must use the Fastgraph
  4646. routine fg_mouseini to initialize the mouse within the program.
  4647. 198   Fastgraph User's Guide
  4648.  
  4649.  
  4650.      The fg_mouseini routine has no arguments and returns a "success or
  4651. failure" indicator as its function value.  If the return value is -1, it
  4652. means fg_mouseini could not initialize the mouse (either because the mouse
  4653. driver is not installed, or the driver is installed but the mouse is
  4654. physically disconnected).  The fg_mouseini routine will also return -1 when
  4655. used in video modes 20 and 21 because there is no mouse support available in
  4656. these video modes.  If fg_mouseini returns a positive integer value, then the
  4657. mouse initialization was successful.  The value itself indicates the number
  4658. of buttons (either 2 or 3) on the mouse.  If you don't call fg_mouseini, or
  4659. if fg_mouseini can't initialize the mouse, none of Fastgraph's other mouse
  4660. support routines will have any effect.
  4661.  
  4662.      Example 12-5 illustrates how to initialize the mouse.  Unlike the
  4663. keyboard support routines, Fastgraph's mouse support routines require that
  4664. fg_setmode first be called.  In this example, we simply pass fg_setmode the
  4665. value -1 to initialize Fastgraph for whatever video mode is in effect when we
  4666. run the program.  The program then calls fg_mouseini and prints a message
  4667. indicating whether or not the initialization was successful.  If it was, the
  4668. message includes the number of buttons on the mouse.
  4669.  
  4670.                                 Example 12-5.
  4671.  
  4672.                main()
  4673.                {
  4674.                   int status;
  4675.  
  4676.                   fg_setmode(-1);
  4677.                   status = fg_mouseini();
  4678.  
  4679.                   if (status < 0)
  4680.                      printf("Mouse not available.\n");
  4681.                   else
  4682.                      printf("%d button mouse found.\n",status);
  4683.                }
  4684.  
  4685.  
  4686.      You should be aware that certain Microsoft-compatible mouse drivers do
  4687. not fully initialize the mouse when a program changes video modes.  This
  4688. problem most frequently occurs when you restore the original video mode at
  4689. the end of a program that has called fg_mouseini.  When changing video modes,
  4690. it is recommended that you first make the mouse cursor invisible (this is
  4691. described in the next section), change the video mode, and then call
  4692. fg_mouseini again to initialize the mouse for the new video mode.
  4693.  
  4694.  
  4695. Controlling the Mouse Cursor
  4696.  
  4697.      The mouse cursor indicates the current position of the mouse.  By
  4698. default, the cursor is a small white arrow in graphics modes and a one-
  4699. character rectangle in text modes.  After you use fg_mouseini to initialize
  4700. the mouse, the mouse cursor is invisible.  To make it visible, you must use
  4701. the fg_mousevis routine.  This routine has a single integer argument that
  4702. defines the mouse cursor visibility.  If it is 0, the mouse cursor will be
  4703. invisible; if it is 1, the mouse cursor becomes visible.
  4704.                                        Chapter 12:  Input Device Support   199
  4705.  
  4706.      If the mouse cursor is in an area of the screen that is being updated,
  4707. or if it moves into this area during the update process, you must make the
  4708. mouse cursor invisible.  Instead of checking if the mouse cursor is within
  4709. such an area, it is generally more convenient just to make the mouse cursor
  4710. invisible during screen updates and then make it visible again when the
  4711. updating is finished.
  4712.  
  4713.      After you initialize the mouse, the cursor is positioned in the center
  4714. of the screen.  Moving the mouse of course changes the cursor position, but
  4715. you can also position the mouse cursor with the Fastgraph routine
  4716. fg_mousemov.  This routine has two arguments that specify the new horizontal
  4717. and vertical cursor position.  The position is expressed in screen space
  4718. units for graphics modes, while it is expressed in character cells for text
  4719. modes.  The fg_mousemov routine moves the cursor whether or not it is
  4720. currently visible.
  4721.  
  4722.      Sometimes it is useful to restrict the mouse cursor to a specific area
  4723. of the screen.  The Fastgraph routine fg_mouselim prevents the mouse cursor
  4724. from moving outside the specified rectangular area.  It requires four
  4725. arguments that specify the minimum horizontal coordinate, maximum horizontal
  4726. coordinate, minimum vertical coordinate, and maximum vertical coordinate of
  4727. this area.  Again, the coordinates are expressed in screen space units for
  4728. graphics modes and character cells for text modes.
  4729.  
  4730.      One of the most important functions of the mouse driver is to translate
  4731. the horizontal and vertical mouse movements into a position on the screen.
  4732. The mouse reports these movements to the mouse driver in units called mickeys
  4733. (one mickey is about 1/200 of an inch).  By default, moving the mouse 8
  4734. mickeys in the horizontal direction moves the mouse cursor one horizontal
  4735. pixel.  Similarly, moving the mouse 16 mickeys vertically moves the cursor
  4736. one vertical pixel.  Fastgraph provides a routine named fg_mousespd that can
  4737. change these values, which effectively allows you to control the speed at
  4738. which the mouse cursor moves relative to the movement of the mouse itself.
  4739. The fg_mousespd routine requires two arguments that define the number of
  4740. mickeys required for eight pixels of mouse cursor movement.  The first
  4741. argument specifies this for the horizontal direction, and the second for the
  4742. vertical direction.
  4743.  
  4744.      Example 12-6, which runs in any graphics mode, demonstrates the
  4745. fg_mousevis, fg_mousemov, fg_mouselim, and fg_mousespd routines.  The program
  4746. first establishes the video mode, initializes the mouse, and fills the screen
  4747. with a white rectangle.  Next, the program calls fg_mousevis to make the
  4748. mouse cursor visible and then calls fg_mouselim to restrict the mouse cursor
  4749. to an area one-fourth the size of the screen, centered in the middle of the
  4750. screen.  At this point you should move the mouse cursor around the screen to
  4751. see the effect of fg_mouselim and note the speed at which the cursor moves
  4752. relative to the mouse itself.  The program continues when you press any key.
  4753.  
  4754.      The program then uses fg_mousemov to move the mouse cursor to each
  4755. corner of the region established by fg_mouselim.  The call to fg_waitfor
  4756. keeps the cursor in each corner for two seconds, unless you move the mouse.
  4757. Note how the program tries to move the mouse cursor to each corner of the
  4758. screen, but since doing so would move the cursor outside the defined region
  4759. of movement, fg_mousemov just positions the cursor at the nearest point
  4760. possible within this region.  The last call to fg_mousemov moves the cursor
  4761. back to the middle of the screen.  After doing this, the program calls
  4762. fg_mousespd to change the mouse cursor speed.  The values passed to
  4763. 200   Fastgraph User's Guide
  4764.  
  4765. fg_mousespd (16 and 32) are twice the defaults and therefore make you move
  4766. the mouse twice as far as before to move the mouse cursor the same distance.
  4767. When you run the program, compare the mouse sensitivity to the original
  4768. speed.  After a keystroke, the program returns to DOS.
  4769.  
  4770.                                 Example 12-6.
  4771.  
  4772.                main()
  4773.                {
  4774.                   int maxx, maxy;
  4775.                   int old_mode;
  4776.  
  4777.                   old_mode = fg_getmode();
  4778.                   fg_setmode(fg_automode());
  4779.  
  4780.                   if (fg_mouseini() < 0) {
  4781.                      fg_setmode(old_mode);
  4782.                      fg_reset();
  4783.                      exit();
  4784.                      }
  4785.  
  4786.                   maxx = fg_getmaxx();
  4787.                   maxy = fg_getmaxy();
  4788.                   fg_setcolor(15);
  4789.                   fg_rect(0,maxx,0,maxy);
  4790.  
  4791.                   fg_mousevis(1);
  4792.                   fg_mouselim(maxx/4,3*maxx/4,maxy/4,3*maxy/4);
  4793.                   fg_waitkey();
  4794.  
  4795.                   fg_mousemov(0,0);
  4796.                   fg_waitfor(36);
  4797.                   fg_mousemov(maxx,0);
  4798.                   fg_waitfor(36);
  4799.                   fg_mousemov(maxx,maxy);
  4800.                   fg_waitfor(36);
  4801.                   fg_mousemov(0,maxy);
  4802.                   fg_waitfor(36);
  4803.                   fg_mousemov(maxx/2,maxy/2);
  4804.                   fg_mousespd(16,32);
  4805.                   fg_waitkey();
  4806.  
  4807.                   fg_setmode(old_mode);
  4808.                   fg_reset();
  4809.                }
  4810.  
  4811.  
  4812.  
  4813. Reporting the Mouse Status
  4814.  
  4815.      It is obviously important to be able to track the mouse position and
  4816. button status.  The Fastgraph routines fg_mousepos and fg_mousebut enable you
  4817. to do this.
  4818.  
  4819.      The fg_mousepos routine returns information about the current mouse
  4820. cursor position and button status.  It requires three integer arguments, all
  4821. passed by reference.  The first two arguments respectively receive the
  4822.                                        Chapter 12:  Input Device Support   201
  4823.  
  4824. horizontal and vertical coordinates of the mouse cursor.  These values are
  4825. expressed in screen space units for graphics modes and character cells for
  4826. text modes.  The third argument receives a three-bit mask containing the
  4827. button status as indicated below.
  4828.  
  4829.  
  4830.                   bit
  4831.                 number  meaning
  4832.  
  4833.                    0    1 if left button pressed, 0 if not
  4834.                    1    1 if right button pressed, 0 if not
  4835.                    2    1 if middle button pressed, 0 if not
  4836.  
  4837.  
  4838. For example, if both the left and right buttons are pressed, the button
  4839. status will be set to 3.  If the mouse only has two buttons, bit 2 will
  4840. always be zero.
  4841.  
  4842.      Another routine, fg_mousebut, is available for returning the number of
  4843. button press or release counts that have occurred since the last check, or
  4844. since calling fg_mouseini.  Each mouse button maintains its own separate
  4845. counters, so fg_mousebut returns this information for a specific button.
  4846. Additionally, fg_mousebut returns the horizontal and vertical position of the
  4847. mouse cursor at the time the specified button was last pressed or released.
  4848.  
  4849.      The fg_mousebut routine takes four integer arguments, of which the last
  4850. three are passed by reference.  The first argument specifies the button of
  4851. interest (1 means the left button, 2 is the right button, and 3 is the middle
  4852. button).  If this value is positive, button press counts will be reported.
  4853. If it is negative, release counts will be reported.  The second, third, and
  4854. fourth arguments respectively receive the press or release count, the
  4855. horizontal mouse cursor position at the time of the last press or release,
  4856. and the vertical position at that same time.  If the press or release count
  4857. is zero, the mouse cursor position is returned as (0,0).  The coordinate
  4858. positions are expressed in screen space units for graphics modes and
  4859. character cells for text modes.
  4860.  
  4861.      Example 12-7 runs in any graphics video mode and illustrates the use of
  4862. the fg_mousepos and fg_mousebut routines.  The program first establishes the
  4863. video mode and then initializes the mouse (the program exits if the
  4864. initialization fails).  It next fills the entire screen with a white
  4865. rectangle and then calls fg_mousevis to make the mouse cursor visible.
  4866.  
  4867.      The main part of example 12-7 is a while loop that polls the mouse at
  4868. three-second intervals (the call fg_waitfor(54) delays the program for three
  4869. seconds).  Within the loop, the program first uses fg_mousebut to obtain the
  4870. number of times the left mouse button was pressed in the last three seconds.
  4871. Following this, the fg_mousepos routine obtains the current mouse position.
  4872. The program then displays this information in the upper left corner of the
  4873. screen; note how fg_mousevis is used to make the cursor invisible during
  4874. graphics operations.  The program continues until you press the right mouse
  4875. button, checked by the call to fg_mousebut at the end of the loop.
  4876.  
  4877. 202   Fastgraph User's Guide
  4878.                                 Example 12-7.
  4879.  
  4880.           main()
  4881.           {
  4882.              int old_mode;
  4883.              int buttons, count;
  4884.              int x, y;
  4885.              char string[25];
  4886.  
  4887.              old_mode = fg_getmode();
  4888.              fg_setmode(fg_automode());
  4889.  
  4890.              if (fg_mouseini() < 0) {
  4891.                 fg_setmode(old_mode);
  4892.                 fg_reset();
  4893.                 exit();
  4894.                 }
  4895.  
  4896.              fg_setcolor(15);
  4897.              fg_rect(0,fg_getmaxx(),0,fg_getmaxy());
  4898.  
  4899.              fg_mousevis(1);
  4900.              count = 0;
  4901.  
  4902.              while (count == 0) {
  4903.                 fg_waitfor(54);
  4904.                 fg_mousebut(1,&count,&x,&y);
  4905.                 fg_mousepos(&x,&y,&buttons);
  4906.                 sprintf(string,"X=%3d  Y=%3d  count=%4d",x,y,count);
  4907.                 fg_mousevis(0);
  4908.                 fg_setcolor(15);
  4909.                 fg_rect(0,fg_xconvert(25),0,fg_yconvert(1));
  4910.                 fg_setcolor(0);
  4911.                 fg_locate(0,0);
  4912.                 fg_text(string,24);
  4913.                 fg_mousevis(1);
  4914.                 fg_mousebut(2,&count,&x,&y);
  4915.                 }
  4916.  
  4917.              fg_setmode(old_mode);
  4918.              fg_reset();
  4919.           }
  4920.  
  4921.  
  4922.  
  4923. Defining the Mouse Cursor
  4924.  
  4925.      By default, the mouse cursor is a small white arrow in graphics modes
  4926. and a one-character rectangle in text modes.  In graphics modes, you can
  4927. change the mouse cursor to any 16 by 16 pixel image with the Fastgraph
  4928. routine fg_mouseptr (in the CGA four-color graphics modes, the cursor size is
  4929. 8 by 16 pixels).  You cannot change the mouse cursor shape in text modes, but
  4930. you can use the Fastgraph routine fg_mousecur to define how it interacts with
  4931. existing characters on the screen.
  4932.                                        Chapter 12:  Input Device Support   203
  4933.  
  4934. Text Modes
  4935.  
  4936.      To change the mouse cursor in text modes, you must first define two 16-
  4937. bit quantities called the screen mask and cursor mask.  The following figure
  4938. defines the format of each mask.
  4939.  
  4940.  
  4941.                        bits      meaning
  4942.  
  4943.                        0 to 7    ASCII character value
  4944.                        8 to 11   foreground color
  4945.                        12 to 14  background color
  4946.                        15        blink
  4947.  
  4948.  
  4949. Notice how this structure parallels the character and attribute bytes
  4950. associated with each character cell.  The default screen mask is 77FF hex,
  4951. and the default cursor mask is 7700 hex.
  4952.  
  4953.      When you position the mouse over a specific character cell, the mouse
  4954. driver uses the current screen and cursor masks to determine the mouse
  4955. cursor's appearance.  First, the mouse driver logically ANDs the screen mask
  4956. with the existing contents of that character cell.  It then XORs that result
  4957. with the cursor mask to display the mouse cursor.
  4958.  
  4959.      For example, consider how the mouse cursor is produced in the 80-column
  4960. color text mode (mode 3).  Suppose a specific character cell contains the
  4961. ASCII character 0 (48 decimal, 30 hex) and an attribute byte that specifies a
  4962. white (color 15) foreground on a blue background (color 1) and does not blink
  4963. (blink bit 0).  The binary structure of the character and its attribute are:
  4964.  
  4965.                             attribute    character
  4966.  
  4967.                             0 001 1111   00110000
  4968.  
  4969.  
  4970. Now let's see what happens when we apply the screen and cursor masks to the
  4971. character and its attribute.
  4972.  
  4973.  
  4974.             attribute/character   0001 1111 0011 0000   (1F30 hex)
  4975.             default screen mask   0111 0111 1111 1111   (77FF hex)
  4976.                                   -------------------
  4977.             result of AND         0001 0111 0011 0000   (1730 hex)
  4978.             default cursor mask   0111 0111 0000 0000   (7700 hex)
  4979.                                   -------------------
  4980.             result of XOR         0110 0000 0011 0000   (6030 hex)
  4981.  
  4982.  
  4983. The resulting character (30 hex) is the original character, but the new
  4984. attribute (60 hex) represents a black foreground with a brown background and
  4985. does not blink.  As long as the mouse cursor remains positioned on this
  4986. character cell, it would appear black on brown.
  4987. 204   Fastgraph User's Guide
  4988.  
  4989.      When we use the default screen and cursor masks, the mouse cursor will
  4990. always display the original character and it will not blink.  The cursor
  4991. foreground color will be 15-F, where F is the displayed character's
  4992. foreground color.  Similarly, the cursor background color will be 7-B, where
  4993. B is the displayed character's background color.  The default masks will
  4994. virtually always produce a satisfactory mouse cursor.
  4995.  
  4996.      It is possible, however, to change the appearance of the mouse cursor in
  4997. text modes by using your own screen and cursor masks.  The Fastgraph routine
  4998. fg_mousecur does just that.  It expects two arguments, the first being the
  4999. cursor mask and the second the screen mask.  Example 12-8 demonstrates the
  5000. use of fg_mousecur.  The program displays some text and uses the default
  5001. mouse cursor.  After waiting for a keystroke, the program calls fg_mousecur
  5002. to define a new mouse cursor.  The new cursor is similar to the default
  5003. cursor, but it displays the foreground colors in the opposite intensity as
  5004. the default cursor.  The program then waits for another keystroke before
  5005. returning to DOS.
  5006.  
  5007.                                 Example 12-8.
  5008.  
  5009.                   main()
  5010.                   {
  5011.                      int old_mode;
  5012.                      int row;
  5013.  
  5014.                      old_mode = fg_getmode();
  5015.                      fg_setmode(3);
  5016.  
  5017.                      if (fg_mouseini() < 0) {
  5018.                         fg_setmode(old_mode);
  5019.                         fg_reset();
  5020.                         exit();
  5021.                         }
  5022.  
  5023.                      fg_setattr(7,0,0);
  5024.                      fg_rect(0,fg_getmaxx(),0,fg_getmaxy());
  5025.  
  5026.                      fg_setattr(12,7,0);
  5027.                      for (row = 0; row < 25; row++) {
  5028.                         fg_locate(row,34);
  5029.                         fg_text("example 12-8",12);
  5030.                         }
  5031.  
  5032.                      fg_mousevis(1);
  5033.                      fg_waitkey();
  5034.                      fg_mousecur(0x7FFF,0x7F00);
  5035.                      fg_waitkey();
  5036.  
  5037.                      fg_setmode(old_mode);
  5038.                      fg_reset();
  5039.                   }
  5040.  
  5041.                                        Chapter 12:  Input Device Support   205
  5042.  
  5043. Graphics Modes
  5044.  
  5045.      Defining the mouse cursor in graphics video modes also requires creating
  5046. a screen mask and cursor mask, but as one might expect, the structure of
  5047. these masks is vastly different than for text modes.  In fact, it closely
  5048. resembles the mode-independent bit map format used by the fg_drawmap routine.
  5049. Although their structure differs, the way the mouse driver uses the masks is
  5050. the same as in the text modes.  That is, the driver displays the mouse cursor
  5051. by first logically ANDing video memory with the screen mask, and then XORing
  5052. that result with the cursor mask.
  5053.  
  5054.      Let's begin by looking at the masks for the default mouse cursor in
  5055. graphics modes.  The size of each mask (and hence the mouse cursor) is 16
  5056. pixels wide and 16 pixels high.  As mentioned earlier, the default cursor is
  5057. a small white arrow with a black outline around it.  Here are its screen and
  5058. cursor masks expressed as binary values.
  5059.  
  5060.             screen                  cursor                  cursor
  5061.              mask                    mask                 appearance
  5062.  
  5063.        1001111111111111        0000000000000000         **
  5064.        1000111111111111        0010000000000000         *x*
  5065.        1000011111111111        0011000000000000         *xx*
  5066.        1000001111111111        0011100000000000         *xxx*
  5067.        1000000111111111        0011110000000000         *xxxx*
  5068.        1000000011111111        0011111000000000         *xxxxx*
  5069.        1000000001111111        0011111100000000         *xxxxxx*
  5070.        1000000000111111        0011111110000000         *xxxxxxx*
  5071.        1000000000011111        0011111111000000         *xxxxxxxx*
  5072.        1000000000001111        0011111000000000         *xxxxx*****
  5073.        1000000011111111        0011011000000000         *xx*xx*
  5074.        1000100001111111        0010001100000000         *x* *xx*
  5075.        1001100001111111        0000001100000000         **  *xx*
  5076.        1111110000111111        0000000110000000              *xx*
  5077.        1111110000111111        0000000110000000              *xx*
  5078.        1111111000111111        0000000000000000               ***
  5079.  
  5080.      The mouse driver first ANDs the screen mask with video memory at the
  5081. mouse cursor position.  This means the screen mask 1 bits leave video memory
  5082. intact, while the 0 bits change the corresponding pixels to black.  Next, the
  5083. mouse driver XORs the result with the cursor mask.  This time the cursor mask
  5084. 0 bits leave video memory unchanged, while the 1 bits change the
  5085. corresponding pixels to white.  This produces a mouse cursor as shown above
  5086. on the right, where a dot ( ) represents an unchanged pixel, an asterisk (*)
  5087. a black pixel, and an x a white pixel.  The following table summarizes the
  5088. cursor appearance for all possible combinations of mask bits.
  5089.  
  5090.  
  5091.            screen mask bit  cursor mask bit  resulting cursor pixel
  5092.  
  5093.                   0                0         black
  5094.                   0                1         white
  5095.                   1                0         unchanged
  5096.                   1                1         inverted
  5097.  
  5098. 206   Fastgraph User's Guide
  5099.  
  5100.      The color of an "inverted" pixel is n-k, where n is the maximum color
  5101. number in the current video mode, and k is the color of the pixel being
  5102. replaced.  Also, "black" and "white" pixels are not necessarily these colors
  5103. in 16-color and 256-color modes.  More correctly, "black" pixels are
  5104. displayed in the color assigned to palette 0, and "white" pixels are the
  5105. displayed in the color assigned to palette 15.  If you're using the CGA color
  5106. modes, "black" pixels are displayed in the background color, and "white"
  5107. pixels appear in color 3 (whose actual color is determined by the selected
  5108. CGA palette).
  5109.  
  5110.      With an understanding of the way the default mouse cursor works in
  5111. graphics modes, we're now ready to define our own mouse cursor.  Shown below
  5112. are the screen mask, cursor mask, and resulting appearance for a solid plus-
  5113. shaped cursor.  The hexadecimal equivalents of the binary mask values are
  5114. also given.
  5115.  
  5116.    ----- screen mask ----      ----- cursor mask ----
  5117.                                                                 cursor
  5118.         binary       hex            binary       hex          appearance
  5119.  
  5120.    1110000000111111  E03F      0000000000000000  0000      ...*******......
  5121.    1110000000111111  E03F      0000111110000000  0F80      ...*xxxxx*......
  5122.    1110000000111111  E03F      0000111110000000  0F80      ...*xxxxx*......
  5123.    0000000000000111  0007      0000111110000000  0F80      ****xxxxx****...
  5124.    0000000000000111  0007      0111111111110000  7FF0      *xxxxxxxxxxx*...
  5125.    0000000000000111  0007      0111111111110000  7FF0      *xxxxxxxxxxx*...
  5126.    0000000000000111  0007      0111111111110000  7FF0      *xxxxxxxxxxx*...
  5127.    0000000000000111  0007      0111111111110000  7FF0      *xxxxxxxxxxx*...
  5128.    0000000000000111  0007      0111111111110000  7FF0      *xxxxxxxxxxx*...
  5129.    0000000000000111  0007      0000111110000000  0F80      ****xxxxx****...
  5130.    1110000000111111  E03F      0000111110000000  0F80      ...*xxxxx*......
  5131.    1110000000111111  E03F      0000111110000000  0F80      ...*xxxxx*......
  5132.    1110000000111111  E03F      0000000000000000  0000      ...*******......
  5133.    1111111111111111  FFFF      0000000000000000  0000      ................
  5134.    1111111111111111  FFFF      0000000000000000  0000      ................
  5135.    1111111111111111  FFFF      0000000000000000  0000      ................
  5136.  
  5137. If we wanted to make the mouse cursor hollow rather than solid, the masks and
  5138. resulting cursor appearance would look like this.
  5139.                                        Chapter 12:  Input Device Support   207
  5140.  
  5141.    ----- screen mask ----      ----- cursor mask ----
  5142.                                                                 cursor
  5143.         binary       hex            binary       hex          appearance
  5144.  
  5145.    1110000000111111  E03F      0000000000000000  0000      ...*******......
  5146.    1110111110111111  EFBF      0000000000000000  0000      ...*.....*......
  5147.    1110111110111111  EFBF      0000000000000000  0000      ...*.....*......
  5148.    0000111110000111  0F87      0000000000000000  0000      ****.....****...
  5149.    0111111111110111  7FF7      0000000000000000  0000      *...........*...
  5150.    0111111111110111  7FF7      0000000000000000  0000      *...........*...
  5151.    0111111111110111  7FF7      0000001000000000  0200      *.....x.....*...
  5152.    0111111111110111  7FF7      0000000000000000  0000      *...........*...
  5153.    0111111111110111  7FF7      0000000000000000  0000      *...........*...
  5154.    0000111110000111  0F87      0000000000000000  0000      ****.....****...
  5155.    1110111110111111  EFBF      0000000000000000  0000      ...*.....*......
  5156.    1110111110111111  EFBF      0000000000000000  0000      ...*.....*......
  5157.    1110000000111111  E03F      0000000000000000  0000      ...*******......
  5158.    1111111111111111  FFFF      0000000000000000  0000      ................
  5159.    1111111111111111  FFFF      0000000000000000  0000      ................
  5160.    1111111111111111  FFFF      0000000000000000  0000      ................
  5161.  
  5162. Note that the center bit defined in the cursor mask causes the corresponding
  5163. pixel in video memory to be inverted.
  5164.  
  5165.      There is one more item needed to define a graphics mode mouse cursor
  5166. completely.  That item is the hot spot, or the actual screen position used or
  5167. reported by the mouse driver.  For the plus-shaped cursors just constructed,
  5168. it would be sensible to define the hot spot in the center of the plus.  The
  5169. hot spot is specified relative to the upper left corner of the cursor, so its
  5170. position within the cursor would be (6,6) -- that is, six pixels to the right
  5171. and six pixels below the upper left corner.  You can specify the hot spot
  5172. offsets using negative values or values above 15 to position it outside the
  5173. mouse cursor matrix if desired.
  5174.  
  5175.      The Fastgraph routine fg_mouseptr defines a mouse cursor in graphics
  5176. modes.  The first of its three arguments is a 32-element integer array,
  5177. passed by reference.  The array's first 16 elements contain the screen mask,
  5178. and its second 16 elements contain the cursor mask.  The remaining two
  5179. arguments respectively specify the horizontal and vertical offsets for the
  5180. hot spot.  The fg_mouseptr routine has no effect in a text video mode.
  5181.  
  5182.      Example 12-9 is similar to example 12-8.  It shows how to define a
  5183. graphics mode mouse cursor using fg_mouseptr.  The values stored in the solid
  5184. and hollow arrays define the screen and cursor masks for the solid and hollow
  5185. plus-shaped mouse cursors discussed earlier.  After making the mouse cursor
  5186. visible, the program uses the default mouse cursor until a key is pressed.
  5187. Following this, it changes to the solid cursor.  After another keystroke, the
  5188. program changes to the hollow cursor.  The program waits for yet another
  5189. keystroke before returning to DOS.  When you run example 12-9, you should
  5190. compare the physical differences between the three mouse cursors it uses.
  5191.  
  5192. 208   Fastgraph User's Guide
  5193.                                 Example 12-9.
  5194.  
  5195.    int solid[]  = {0xE03F,0xE03F,0xE03F,0x0007,0x0007,0x0007,0x0007,0x0007,
  5196.                    0x0007,0x0007,0xE03F,0xE03F,0xE03F,0xFFFF,0xFFFF,0xFFFF,
  5197.                    0x0000,0x0F80,0x0F80,0x0F80,0x7FF0,0x7FF0,0x7FF0,0x7FF0,
  5198.                    0x7FF0,0x0F80,0x0F80,0x0F80,0x0000,0x0000,0x0000,0x0000};
  5199.  
  5200.    int hollow[] = {0xE03F,0xEFBF,0xEFBF,0x0F87,0x7FF7,0x7FF7,0x7FF7,0x7FF7,
  5201.                    0x7FF7,0x0F87,0xEFBF,0xEFBF,0xE03F,0xFFFF,0xFFFF,0xFFFF,
  5202.                    0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0200,0x0000,
  5203.                    0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000};
  5204.  
  5205.    main()
  5206.    {
  5207.       int old_mode;
  5208.       int column, row, last_row;
  5209.  
  5210.       old_mode = fg_getmode();
  5211.       fg_setmode(fg_automode());
  5212.  
  5213.       if (fg_mouseini() < 0) {
  5214.          fg_setmode(old_mode);
  5215.          fg_reset();
  5216.          exit();
  5217.          }
  5218.  
  5219.       fg_setcolor(15);
  5220.       fg_rect(0,fg_getmaxx(),0,fg_getmaxy());
  5221.  
  5222.       fg_setcolor(12);
  5223.       column = fg_xalpha(fg_getmaxx()/2) - 6;
  5224.       last_row = fg_yalpha(fg_getmaxy()) + 1;
  5225.  
  5226.       for (row = 0; row < last_row; row++) {
  5227.          fg_locate(row,column);
  5228.          fg_text("example 12-9",12);
  5229.          }
  5230.  
  5231.       fg_mousevis(1);
  5232.       fg_waitkey();
  5233.       fg_mouseptr(solid,6,6);
  5234.       fg_waitkey();
  5235.       fg_mouseptr(hollow,6,6);
  5236.       fg_waitkey();
  5237.  
  5238.       fg_setmode(old_mode);
  5239.       fg_reset();
  5240.    }
  5241.  
  5242.                                        Chapter 12:  Input Device Support   209
  5243.  
  5244. CGA Considerations
  5245.  
  5246.      The mouse driver treats the screen and cursor masks differently in the
  5247. CGA four-color graphics modes (modes 4 and 5) than in the other graphics
  5248. modes.  In the CGA modes, each pair of mask bits corresponds to one pixel.
  5249. This means the masks more closely resemble the mode-specific format used by
  5250. fg_drwimage instead of the mode-independent format of fg_drawmap.
  5251.  
  5252.      Fastgraph uses a different default mouse cursor for modes 4 and 5.  Its
  5253. screen and cursor masks, as well as the resulting cursor appearance, are
  5254. shown in the following diagram.
  5255. 210   Fastgraph User's Guide
  5256.  
  5257.                 screen                  cursor              cursor
  5258.                 mask                    mask             appearance
  5259.  
  5260.            0000111111111111        0000000000000000        **
  5261.            0000001111111111        0011000000000000        ***
  5262.            0000000011111111        0011110000000000        ****
  5263.            0000000000111111        0011111100000000        *****
  5264.            0000000000001111        0011111111000000        ******
  5265.            0000000000000011        0011111111110000        *******
  5266.            0000000000000011        0011111100000000        *******
  5267.            0000000000111111        0011111110000000        *****
  5268.            0000000000001111        0011000011000000        ******
  5269.            0000110000001111        0000000011000000        ** ***
  5270.            1111111100000011        0000000000110000            ***
  5271.            1111111100000011        0010000000110000            ***
  5272.            1111111111000011        0000000000000000             **
  5273.            1111111111111111        0000000000000000
  5274.            1111111111111111        0000000000000000
  5275.            1111111111111111        0000000000000000
  5276.  
  5277. As you can see, the resulting mouse cursor is eight pixels wide instead of
  5278. 16.
  5279.  
  5280.      Another important point concerning mouse cursors in modes 4 and 5 is the
  5281. possibility of pixel bleeding, or the changing of colors within the mouse
  5282. cursor as it moves horizontally.  Bleeding will occur if you use the bit
  5283. pairs 01 or 10 in either mask to represent a pixel.  In the default masks for
  5284. modes 4 and 5, note that only the binary values 00 and 11 appear as bit
  5285. pairs.  Keep this in mind if you create your own masks in these video modes.
  5286.  
  5287.  
  5288. Joystick Support
  5289.  
  5290.      The third type of input device supported by Fastgraph is the joystick.
  5291. Although joysticks are not as popular as mice, they generally are preferable
  5292. in cases where a user's reactions are critical, such as in an arcade-style
  5293. game.  Fastgraph includes routines for initializing a joystick, reading a
  5294. joystick's position or button status, and making a joystick behave
  5295. analogously to the keyboard.  These routines are independent of the rest of
  5296. Fastgraph and thus do not require that you first call the fg_setmode routine.
  5297.  
  5298.      Joysticks are connected to a system by means of a game port.  The PCjr
  5299. and Tandy 1000 systems come equipped with two game ports, and hence support
  5300. two joysticks.  On other systems in the IBM family, you can install a game
  5301. port card that contains either one or two game ports.  If the card only has
  5302. one game port, you can use a splitter cable to fork two joysticks into the
  5303. port.
  5304.  
  5305.  
  5306. Initializing Joysticks
  5307.  
  5308.      Before you can use any of Fastgraph's joystick support routines with a
  5309. specific joystick, you must initialize that joystick.  The fg_initjoy routine
  5310. performs this task.  This routine requires a single integer argument that
  5311. specifies which joystick to initialize, either 1 or 2.  If successful,
  5312. fg_initjoy returns 0 as the function value.  If the machine has no game port,
  5313. or if the requested joystick is not connected to the game port, fg_initjoy
  5314.                                        Chapter 12:  Input Device Support   211
  5315.  
  5316. returns -1.  When you use fg_initjoy, the joystick being initialized must be
  5317. centered (that is, the stick itself must not be tilted in either direction).
  5318.  
  5319.      Example 12-10 uses the fg_initjoy routine to attempt to initialize both
  5320. joysticks.  For each joystick, the program prints a message stating whether
  5321. or not the initialization was successful.
  5322.  
  5323.                                 Example 12-10.
  5324.  
  5325.                  main()
  5326.                  {
  5327.                     if (fg_initjoy(1) < 0)
  5328.                        printf("Joystick 1 not available.\n");
  5329.                     else
  5330.                        printf("Joystick 1 found.\n");
  5331.  
  5332.                     if (fg_initjoy(2) < 0)
  5333.                        printf("Joystick 2 not available.\n");
  5334.                     else
  5335.                        printf("Joystick 2 found.\n");
  5336.                  }
  5337.  
  5338.  
  5339.  
  5340. Reporting Joystick Status
  5341.  
  5342.      Each joystick is capable of reporting three items:  its horizontal
  5343. position, its vertical position, and the button status.  Fastgraph includes
  5344. routines for obtaining each of these quantities.
  5345.  
  5346.      The fg_getxjoy and fg_getyjoy routines respectively return the
  5347. horizontal and vertical position of the indicated joystick.  Both routines
  5348. require a single integer argument, whose value is either 1 or 2, to identify
  5349. the joystick.  The requested position is returned as the function value.
  5350. Horizontal coordinates increase as the joystick moves to the right, while
  5351. vertical coordinates increase as the joystick moves downward.  If fg_initjoy
  5352. failed to initialize the specified joystick, or if your program hasn't yet
  5353. called fg_initjoy, both fg_getxjoy and fg_getyjoy will return the value -1.
  5354.  
  5355.      Joystick characteristics vary more than those of any other input device.
  5356. The values returned by fg_getxjoy and fg_getyjoy depend on the system's
  5357. processor speed and the brand of joystick used.  It often suffices just to
  5358. know the joystick position relative to its previous position, in which case
  5359. the actual coordinate values do not matter.  However, if you must rely on
  5360. specific coordinate values, your program must perform some type of manual
  5361. joystick calibration and then scale the coordinates reported by fg_getxjoy
  5362. and fg_getyjoy as needed.
  5363.  
  5364.      The other piece of information joysticks provide is the button status.
  5365. Most joysticks have two buttons, called the top and bottom buttons.  Others
  5366. have three buttons, but one of them duplicates the functionality of another
  5367. (for example, a joystick might have one bottom button on its left side and
  5368. another on its right side).  The Fastgraph routine fg_button returns the
  5369. joystick button status as its function value.  Like fg_getxjoy and
  5370. fg_getyjoy, the fg_button routine requires a single argument that specifies
  5371. the joystick number.  The meaning of the returned value is shown below.
  5372. 212   Fastgraph User's Guide
  5373.  
  5374.  
  5375.                     value  meaning
  5376.  
  5377.                       0    neither button pressed
  5378.                       1    top button pressed
  5379.                       2    bottom button pressed
  5380.                       3    top and bottom buttons pressed
  5381.  
  5382.  
  5383.      It is not necessary to call fg_initjoy before using fg_button.  If the
  5384. specified joystick is not present, the fg_button routine will return a value
  5385. of 0.
  5386.  
  5387.      Example 12-11 uses fg_getxjoy, fg_getyjoy, and fg_button to poll both
  5388. joysticks at half-second intervals.  It then displays the joystick number (1
  5389. or 2), horizontal position, vertical position, and button status for each
  5390. joystick.  As the program runs, you can move the joysticks and watch how the
  5391. movements affect the displayed coordinate values.  The program continues
  5392. doing this until you press Ctrl/C or Ctrl/Break to stop it.
  5393.  
  5394.                                 Example 12-11.
  5395.  
  5396.                   main()
  5397.                   {
  5398.                      int b, x, y;
  5399.  
  5400.                      fg_initjoy(1);
  5401.                      fg_initjoy(2);
  5402.  
  5403.                      while (1) {
  5404.                         x = fg_getxjoy(1);
  5405.                         y = fg_getyjoy(1);
  5406.                         b = fg_button(1);
  5407.                         printf("1:  %3d %3d %1d\n",x,y,b);
  5408.                         x = fg_getxjoy(2);
  5409.                         y = fg_getyjoy(2);
  5410.                         b = fg_button(2);
  5411.                         printf("2:  %3d %3d %1d\n\n",x,y,b);
  5412.                         fg_waitfor(9);
  5413.                         }
  5414.                   }
  5415.  
  5416.  
  5417.      There are two ways of effectively monitoring joystick button status.
  5418. One is to call fg_button at numerous places in your program and then take
  5419. appropriate action depending on the button status.  However, the preferable
  5420. method is to extend the BIOS time-of-day interrupt to check the button status
  5421. at each clock tick (there are 18.2 clock ticks per second), set a flag if a
  5422. button is pressed, and then check the flag as needed in your program.
  5423. Information on modifying the BIOS time-of-day interrupt appears in Appendix C
  5424. of this document.
  5425.                                        Chapter 12:  Input Device Support   213
  5426.  
  5427. Keyboard Emulation
  5428.  
  5429.      Although we can use the fg_getxjoy and fg_getyjoy routines to monitor
  5430. relative joystick movements, it is usually easier to do this with another
  5431. Fastgraph routine, fg_intjoy.  This routine is similar to the fg_intkey
  5432. routine in that it returns two values that are equivalent to the standard or
  5433. extended keyboard codes for analogous keystrokes.
  5434.  
  5435.      The fg_intjoy routine needs three arguments.  The first argument
  5436. specifies the joystick number, either 1 or 2.  The second and third
  5437. arguments, both one-byte quantities passed by reference, receive the standard
  5438. and extended keyboard codes analogous to the joystick movement and button
  5439. status.  The second argument receives a value of 13 (the standard keyboard
  5440. code for the Enter key) if any joystick button is pressed; it receives a
  5441. value of 0 if not.  The third argument receives a value corresponding to the
  5442. extended keyboard code for one of the directional keys on the numeric keypad,
  5443. as summarized in the following table.
  5444.  
  5445.            joystick position  corresponding key  extended key code
  5446.  
  5447.            up and left        Home               71
  5448.            up                 up arrow           72
  5449.            up and right       PgUp               73
  5450.            left               left arrow         75
  5451.            centered           (no action)         0
  5452.            right              right arrow        77
  5453.            down and left      End                79
  5454.            down               down arrow         80
  5455.            down and right     PgDn               81
  5456.  
  5457. The fg_intjoy routine will set both key code arguments to zero if the
  5458. specified joystick has not yet been initialized.
  5459.  
  5460.      Example 12-12 is similar to example 12-10, but it uses fg_intjoy in
  5461. place of fg_getxjoy and fg_getyjoy to report relative joystick position.
  5462. This program does not report the joystick button status as example 12-10
  5463. does, but you could readily add this feature to it.
  5464.  
  5465.                                 Example 12-12.
  5466.  
  5467.                    main()
  5468.                    {
  5469.                       char key, aux;
  5470.  
  5471.                       fg_initjoy(1);
  5472.                       fg_initjoy(2);
  5473.  
  5474.                       while (1) {
  5475.                          fg_intjoy(1,&key,&aux);
  5476.                          printf("1: %2d %2d\n",key,aux);
  5477.                          fg_intjoy(2,&key,&aux);
  5478.                          printf("2: %2d %2d\n\n",key,aux);
  5479.                          fg_waitfor(9);
  5480.                          }
  5481.                    }
  5482.  
  5483. 214   Fastgraph User's Guide
  5484.  
  5485.  
  5486.  
  5487.  
  5488. Special Joystick Considerations
  5489.  
  5490.      If you develop a program that supports only one joystick, you should use
  5491. joystick 1.  The reasons for this are twofold.  First, it will make your
  5492. program consistent with most other products that support joysticks.  Second,
  5493. and perhaps more importantly, many Tandy 1000 series machines cannot
  5494. determine if joystick 2 is present when neither joystick is connected.  This
  5495. means if you use joystick 2 instead of joystick 1 in a single joystick
  5496. program, you won't be able to tell if a joystick is available when running on
  5497. a Tandy 1000.
  5498.  
  5499.  
  5500. Summary of Input Routines
  5501.  
  5502.      This section summarizes the functional descriptions of the Fastgraph
  5503. routines presented in this chapter.  More detailed information about these
  5504. routines, including their arguments and return values, may be found in the
  5505. Fastgraph Reference Manual.
  5506.  
  5507.      FG_BUTTON returns information about the state of either joystick's
  5508. buttons.
  5509.  
  5510.      FG_CAPSLOCK determines the state of the CapsLock key.
  5511.  
  5512.      FG_GETKEY waits for a keystroke (or reads the next entry from the BIOS
  5513. keyboard buffer). It returns the keystroke's standard or extended keyboard
  5514. code.
  5515.  
  5516.      FG_GETXJOY and FG_GETYJOY return the horizontal and vertical coordinate
  5517. position of the specified joystick.  The actual coordinates depend on the
  5518. processor speed and brand of joystick used.
  5519.  
  5520.      FG_INITJOY initializes joystick 1 or 2 and must be called before using
  5521. fg_getxjoy, fg_getyjoy, or fg_intjoy.  It returns a status code indicating
  5522. whether or not the initialization was successful.
  5523.  
  5524.      FG_INTJOY returns the standard and extended keyboard codes analogous to
  5525. the current position and button status of the specified joystick.
  5526.  
  5527.      FG_INTKEY reads the next entry from the BIOS keyboard buffer and returns
  5528. the keystroke's standard or extended keyboard code.  It is similar to
  5529. fg_getkey, but it does not wait for a keystroke if the keyboard buffer is
  5530. empty.
  5531.  
  5532.      FG_MOUSEBUT returns information about mouse button press or release
  5533. counts, as well as the mouse cursor position at the time of the last button
  5534. press or release.
  5535.  
  5536.      FG_MOUSECUR defines the appearance of the mouse cursor in text video
  5537. modes.
  5538.  
  5539.      FG_MOUSEINI initializes the mouse and must be called before any of
  5540. Fastgraph's other mouse support routines.  It returns an error status if the
  5541. mouse driver has not been loaded, or if the mouse is not connected.
  5542.                                        Chapter 12:  Input Device Support   215
  5543.  
  5544.  
  5545.      FG_MOUSELIM defines the rectangular area in which the mouse cursor may
  5546. move.
  5547.  
  5548.      FG_MOUSEMOV moves the mouse cursor to the specified character cell (in
  5549. text modes) or screen space position (in graphics modes).
  5550.  
  5551.      FG_MOUSEPOS returns the current mouse position and button status.
  5552.  
  5553.      FG_MOUSEPTR defines the shape and appearance of the mouse cursor in
  5554. graphics video modes.
  5555.  
  5556.      FG_MOUSESPD defines the number of mickey units per eight pixels of
  5557. cursor movement.  This effectively controls the speed at which the mouse
  5558. cursor moves relative to the movement of the mouse itself.
  5559.  
  5560.      FG_MOUSEVIS makes the mouse cursor visible or invisible.
  5561.  
  5562.      FG_NUMLOCK determines the state of the NumLock key.
  5563.  
  5564.      FG_SCRLOCK determines the state of the ScrollLock key (which is not
  5565. present on some keyboards).
  5566.  
  5567.      FG_SETCAPS controls the state of the CapsLock key.
  5568.  
  5569.      FG_SETNUM controls the state of the NumLock key.
  5570.  
  5571.      FG_WAITKEY flushes the BIOS keyboard buffer (that is, removes any type-
  5572. ahead characters) and then waits for another keystroke.
  5573. 216   Fastgraph User's Guide
  5574.  
  5575.  
  5576. Chapter 13
  5577.  
  5578. Sound Effects
  5579. 218   Fastgraph User's Guide
  5580.  
  5581.  
  5582. Overview
  5583.  
  5584.      In the realm of the IBM PC and PS/2 family of systems, a sound is
  5585. defined by its frequency, duration, and volume.  The frequency of a sound is
  5586. measured in units called Hertz.  While the PC and PS/2 can produce sounds
  5587. ranging from 18 to over 1 million Hertz, the average human can hear sounds
  5588. between 20 and about 20,000 Hertz.  The length of a sound, called its
  5589. duration, is expressed in clock ticks; there are either 18.2 of 72.8 clock
  5590. ticks per second, depending on the method used to produce the sound.
  5591. Finally, the volume determines the loudness of the sound.  As we'll see in
  5592. this chapter, we can control a sound's volume only on the PCjr and Tandy 1000
  5593. systems.
  5594.  
  5595.      Fastgraph contains several different methods for producing sound
  5596. effects.  These include single tones, a series of tones expressed
  5597. numerically, or a series of tones expressed as musical notes.  The sound
  5598. effects may be discrete, continuous, or performed at the same time as other
  5599. activity.  The sound-related routines are independent of the other parts of
  5600. Fastgraph and do not require any initialization routines be called.
  5601.  
  5602.  
  5603. Sound Sources
  5604.  
  5605.      All members of the PC and PS/2 families can produce sounds using the
  5606. 8253-5 programmable timer chip and the internal speaker.  This method is
  5607. essentially limited to producing single sounds of given frequencies and
  5608. durations, although we can combine these sounds to create interesting audio
  5609. effects or play music.  When we use this technique, we have no control over
  5610. the sound volume.  In fact, sound volumes often vary slightly on different
  5611. systems because the physical properties of the speaker and its housing are
  5612. not always identical.
  5613.  
  5614.      The PCjr and Tandy 1000 systems have an additional, more powerful chip
  5615. for producing sounds.  This is the Texas Instruments SN76496A sound chip,
  5616. called the TI sound chip for short.  The TI sound chip has three independent
  5617. voice channels for producing pure tones, and a fourth channel for generating
  5618. periodic or white noise.  Each voice channel has a separate volume control
  5619. that allows us to control the loudness of the sound it emits.
  5620.  
  5621.  
  5622. Synchronous Sound
  5623.  
  5624.      A sound effect is said to be synchronous if it is produced while no
  5625. other activity is being performed.  In other words, a program makes a
  5626. synchronous sound by starting the sound, waiting for a specified duration,
  5627. and then stopping the sound.  The program must wait for the sound to complete
  5628. before doing anything else.  As long as the duration is relatively short, the
  5629. fact that the sound is synchronous has little or no effect on the program's
  5630. execution speed.  Fastgraph includes routines for producing synchronous sound
  5631. using either the 8253-5 programmable timer or the TI sound chip.
  5632.  
  5633.      The fg_sound routine uses the programmable timer to produce a sound of a
  5634. given frequency and duration.  The frequency, defined by the first argument,
  5635. is expressed in Hertz and must be an integer value between 18 and 32,767.
  5636. The second argument defines the duration and is expressed in clock ticks;
  5637.                                               Chapter 13:  Sound Effects   219
  5638.  
  5639. there are 18.2 clock ticks per second.  If the duration is zero or negative,
  5640. the sound will continue until it is stopped with the fg_quiet routine.
  5641.  
  5642.      Example 13-1 uses the fg_sound routine to create different sound
  5643. effects, pausing for one second between each.  It first produces three
  5644. distinct sounds of 20, 100, and 1,000 Hertz.  Each of these sounds lasts for
  5645. approximately 1/6 of a second (three clock ticks).  The program then makes a
  5646. warbling noise by quickly alternating sounds of similar frequencies.
  5647. Finally, the program creates a sliding tone of increasing frequencies between
  5648. 100 and 500 Hertz.  Each tone in this sequence lasts for two clock ticks, so
  5649. it takes about 4.5 seconds to play the entire sequence.  In all cases,
  5650. example 13-1 displays an identifying message just before each sound.
  5651.  
  5652.                                 Example 13-1.
  5653.  
  5654.               main()
  5655.               {
  5656.                  int freq;
  5657.  
  5658.                  printf("20 Hz tone...\n");
  5659.                  fg_sound(20,3);
  5660.                  fg_waitfor(18);
  5661.  
  5662.                  printf("100 Hz tone...\n");
  5663.                  fg_sound(100,3);
  5664.                  fg_waitfor(18);
  5665.  
  5666.                  printf("1000 Hz tone...\n");
  5667.                  fg_sound(1000,3);
  5668.                  fg_waitfor(18);
  5669.  
  5670.                  printf("warble...\n");
  5671.                  fg_sound(400,1);
  5672.                  fg_sound(410,1);
  5673.                  fg_sound(400,1);
  5674.                  fg_sound(410,1);
  5675.                  fg_waitfor(18);
  5676.  
  5677.                  printf("sliding tone from 100 to 500 Hz...\n");
  5678.                  for (freq = 100; freq <= 500; freq+=10)
  5679.                     fg_sound(freq,2);
  5680.               }
  5681.  
  5682.  
  5683.      The fg_voice routine is analogous to the fg_sound routine, but it uses
  5684. the TI sound chip rather than the programmable timer to create sound.  For
  5685. this reason, the fg_voice routine can only be used on the PCjr or Tandy 1000
  5686. systems.  The TI sound chip allows us to control the volume of a sound, and
  5687. it also offers four distinct voice channels.  Thus, fg_voice requires two
  5688. additional arguments besides frequency and duration to define the voice
  5689. channel and sound volume.
  5690.  
  5691.      The first argument to fg_voice defines the voice channel, as shown
  5692. below.
  5693. 220   Fastgraph User's Guide
  5694.  
  5695.  
  5696.                      value  meaning
  5697.  
  5698.                        1    voice channel #1
  5699.                        2    voice channel #2
  5700.                        3    voice channel #3
  5701.                        4    voice channel #4, periodic noise
  5702.                        5    voice channel #4, white noise
  5703.  
  5704.  
  5705. If we use voice channels 1, 2, or 3, the second argument defines the sound
  5706. frequency in Hertz, between 18 and 32,767.  If we use voice channel 4,
  5707. however, the second argument instead is a value that represents a specific
  5708. frequency, as shown in this table.
  5709.  
  5710.  
  5711.                            value  frequency
  5712.  
  5713.                              0     512 Hertz
  5714.                              1    1024 Hertz
  5715.                              2    2048 Hertz
  5716.  
  5717.  
  5718. The third argument defines the sound volume.  It must be between 0 and 15,
  5719. where 0 is silent and 15 is loudest.  The fourth argument defines the sound
  5720. duration in clock ticks.  As with the fg_sound routine, there are 18.2 clock
  5721. ticks per second, and if the duration is zero or negative, the sound will
  5722. continue until stopped with the fg_quiet routine.
  5723.  
  5724.      Example 13-2 uses the fg_voice routine to create different sound effects
  5725. using the TI sound chip.  As in example 13-1, there is a pause of one second
  5726. between each.  The program first calls the fg_testmode routine to verify it
  5727. is running on a PCjr or Tandy 1000 system (video mode 9 is only available on
  5728. these systems).  If so, the program uses voice channel #4 to produce a 2,048
  5729. Hertz periodic noise, followed by white noise of the same frequency.  Both
  5730. sounds are emitted at the maximum volume level (15) and last for about 1/6 of
  5731. a second each (three clock ticks).  After these noises, example 13-2 produces
  5732. a 500 Hertz tone of increasing volume.  In all cases, the program displays an
  5733. identifying message just before each sound.
  5734.  
  5735.                                 Example 13-2.
  5736.  
  5737.              main()
  5738.              {
  5739.                 int volume;
  5740.  
  5741.                 if (fg_testmode(9,0) == 0) {
  5742.                    printf("This program requires a PCjr or ");
  5743.                    printf("a Tandy 1000 system.\n");
  5744.                    exit();
  5745.                    }
  5746.  
  5747.                                               Chapter 13:  Sound Effects   221
  5748.  
  5749.                 printf("2048 Hz periodic noise...\n");
  5750.                 fg_voice(4,2,15,3);
  5751.                 fg_waitfor(18);
  5752.  
  5753.                 printf("2048 Hz white noise...\n");
  5754.                 fg_voice(5,2,15,3);
  5755.                 fg_waitfor(18);
  5756.  
  5757.                 printf("500 Hz tone of increasing volume...\n");
  5758.                 for (volume = 1; volume <= 15; volume++) {
  5759.                    fg_voice(1,500,volume,0);
  5760.                    fg_waitfor(4);
  5761.                    }
  5762.  
  5763.                 fg_quiet();
  5764.              }
  5765.  
  5766.  
  5767.      Note how example 13-2 uses a duration of zero (continuous sound) and the
  5768. fg_waitfor routine to specify the duration for each volume level the 500
  5769. Hertz tone sequence.  This causes the transition between changes in volume to
  5770. blend better with each other.  The fg_quiet routine, which stops continuous
  5771. sound started with the fg_sound or fg_voice routines, ends the sound after
  5772. the final volume level.
  5773.  
  5774.      The fg_sound and fg_voice routines each produce a single sound.  We've
  5775. seen how we can combine sounds to produce sound effects, but still the
  5776. individual sounds are defined numerically -- that is, by a certain frequency
  5777. and duration.  It is often easier to create sounds in terms of musical notes,
  5778. and for this reason Fastgraph includes a routine fg_music that produces such
  5779. sounds.  The fg_music routine uses the programmable timer to produce
  5780. synchronous sound; it does not support the TI sound chip.
  5781.  
  5782.      The fg_music routine has a single argument called the music string,
  5783. which is passed by reference as a byte array or character string.  The music
  5784. string is simply a variable-length sequence of music commands, followed by a
  5785. dollar-sign ($) terminator.  Music commands are summarized in the following
  5786. table.
  5787.  
  5788.  
  5789.                command   meaning
  5790.  
  5791.                A thru G  Play the specified note in the current
  5792.                          octave.
  5793.  
  5794.                #         May be appended to a note character (A
  5795.                          through G) to make that note sharp.
  5796.  
  5797.                .         May be appended to a note character (A
  5798.                          through G) or a sharp (#) to extend that note
  5799.                          by half its normal length.  Multiple dots may
  5800.                          be used, and each one will again extend the
  5801.                          note by half as much as the previous
  5802.                          extension.
  5803.  
  5804.                Ln        Set the length of subsequent notes and
  5805.                          pauses.  The value of n is an integer between
  5806. 222   Fastgraph User's Guide
  5807.  
  5808.                          1 and 64, where 1 indicates a whole note, 2 a
  5809.                          half note, 4 a quarter note, and so forth.
  5810.                          If no L command is present, L4 is assumed.
  5811.  
  5812.                On        Set the octave for subsequent notes.  The
  5813.                          value of n may be an integer between 0 and 6
  5814.                          to set a specific octave.  It can also be a
  5815.                          plus (+) or minus (-) character to increment
  5816.                          or decrement the current octave number.
  5817.                          Octave 4 contains middle C, and if no O
  5818.                          command is present, O4 is assumed.
  5819.  
  5820.                P         Pause (rest) for the duration specified by
  5821.                          the most recent L command.
  5822.  
  5823.                Sn        Set the amount of silence between notes.  The
  5824.                          value of n is an integer between 0 and 2.  If
  5825.                          n is 0, each note plays for the full period
  5826.                          set by the L command (music legato).  If n is
  5827.                          1, each note plays for 7/8 the period set by
  5828.                          the L command (music normal).  If n is 2,
  5829.                          each note plays for 3/4 the period set by the
  5830.                          L command (music staccato).  If no S command
  5831.                          is present, S1 is assumed.
  5832.  
  5833.                Tn        Set the tempo of the music (the number of
  5834.                          quarter notes per minute).  The value of n is
  5835.                          an integer between 32 and 255.  If no T
  5836.                          command is present, T120 is assumed.
  5837.  
  5838. The fg_music routine ignores any other characters in the music string.  It
  5839. also ignores command values outside the allowable range, such as T20 or O8.
  5840.  
  5841.      Example 13-3 illustrates some uses of the fg_music routine.  The program
  5842. plays the first few bars of "Mary Had a Little Lamb", followed by the musical
  5843. scale (including sharps) in two octaves, and finally the introduction to
  5844. Beethoven's Fifth Symphony.  There is a pause of one second between each
  5845. piece of music, and the program displays the titles before playing the music.
  5846. Blank characters appear in the music strings to help make them more readable.
  5847.  
  5848.                                 Example 13-3.
  5849.  
  5850.  
  5851.     main()
  5852.     {
  5853.        printf("Mary Had a Little Lamb...\n");
  5854.        fg_music("T150 L8 EDCDEEE P DDD P EGG P EDCDEEE L16 P L8 EDDEDC$");
  5855.        fg_waitfor(18);
  5856.  
  5857.        printf("up the scale in two octaves...\n");
  5858.        fg_music("L16 CC#DD#EFF#GG#AA#B O+ CC#DD#EFF#GG#AA#B$");
  5859.        fg_waitfor(18);
  5860.  
  5861.        printf("Beethoven's Fifth Symphony...\n");
  5862.        fg_music("T180 O2 L2 P L8 P GGG L2 D# L24 P L8 P FFF L2 D$");
  5863.     }
  5864.  
  5865.                                               Chapter 13:  Sound Effects   223
  5866.  
  5867.  
  5868.  
  5869. Asynchronous Sound
  5870.  
  5871.      Sounds made concurrently with other activity in a program are said to be
  5872. asynchronous.  Fastgraph's routines that produce asynchronous sound just
  5873. start the sound and then immediately return control to the calling program.
  5874. The sounds will automatically stop when the end of the sequence is reached,
  5875. and you can also stop it on demand before that time.  None of Fastgraph's
  5876. asynchronous sound routines have any effect if there is already asynchronous
  5877. sound in progress.  In addition, the asynchronous sound routines temporarily
  5878. disable the synchronous sound routines (fg_sound, fg_voice, and fg_music)
  5879. while asynchronous sound is in progress.
  5880.  
  5881.      To expand the range of sound effects and to play fast-tempo music,
  5882. Fastgraph temporarily quadruples the clock tick interrupt rate from 18.2 to
  5883. 72.8 ticks per second while producing asynchronous sound.  Because many disk
  5884. controllers rely on the 18.2 tick per second clock rate to synchronize disk
  5885. accesses, your programs should not perform any disk operations when
  5886. asynchronous sound is in progress.
  5887.  
  5888.      The fg_sounds routine is the asynchronous version of the fg_sound
  5889. routine.  It uses the programmable timer to play a sequence of tones
  5890. simultaneous to other operations.  This routine expects as its first argument
  5891. a variable-length integer array, passed by reference, containing pairs of
  5892. frequency and duration values.  As with the fg_sound routine, each frequency
  5893. is expressed in Hertz and must be between 18 and 32,767.  The durations are
  5894. likewise measured in clock ticks, but because the interrupt rate is
  5895. quadrupled, there are 72.8 instead of 18.2 ticks per second.
  5896.  
  5897.      The format of the frequency and duration array passed to fg_sounds is
  5898. shown below.
  5899.  
  5900.  
  5901.                           [0]    frequency of sound 1
  5902.  
  5903.                           [1]    duration  of sound 1
  5904.  
  5905.                           [2]    frequency of sound 2
  5906.  
  5907.                           [3]    duration  of sound 2
  5908.                            .
  5909.                            .
  5910.                            .
  5911.  
  5912.                        [2n-2]    frequency of sound n
  5913.  
  5914.                        [2n-1]    duration  of sound n
  5915.  
  5916.                          [2n]       terminator (0)
  5917.  
  5918.  
  5919. Note that a null character (that is, a zero byte) terminates the array.  The
  5920. second argument passed to fg_sounds is an integer value indicating the number
  5921. of times to cycle through the frequency and duration array.  If this value is
  5922. 224   Fastgraph User's Guide
  5923.  
  5924. negative, the sounds will continue until stopped with the fg_hush or
  5925. fg_hushnext routines.
  5926.  
  5927.      Example 13-4 uses the fg_sounds routine to play the 100 to 500 Hertz
  5928. sliding tone sequence of example 13-1.  To demonstrate the sounds are being
  5929. made concurrently with other operations, messages are displayed while the
  5930. sequence is playing.  This is controlled by the Fastgraph routine fg_playing,
  5931. which returns a value of 1 if asynchronous sounds are in progress, and 0 if
  5932. not.  Note how the duration must be specified as 8 clock ticks (instead of 2
  5933. as in example 13-1) to compensate for the quadrupled clock tick interrupt
  5934. rate.
  5935.  
  5936.                                 Example 13-4.
  5937.  
  5938.                  main()
  5939.                  {
  5940.                     int i;
  5941.                     int freq;
  5942.                     int sound_array[83];
  5943.  
  5944.                     i = 0;
  5945.  
  5946.                     for (freq = 100; freq <= 500; freq+=10) {
  5947.                        sound_array[i++] = freq;
  5948.                        sound_array[i++] = 8;
  5949.                        }
  5950.                     sound_array[i] = 0;
  5951.  
  5952.                     fg_sounds(sound_array,1);
  5953.  
  5954.                     while(fg_playing())
  5955.                        printf("Still playing...\n");
  5956.                  }
  5957.  
  5958.  
  5959.      Just as the fg_sounds routine is analogous to the fg_sound routine,
  5960. there is a Fastgraph routine fg_voices that is similar to the fg_voice
  5961. routine.  That is, fg_voices uses the TI sound chip to play an asynchronous
  5962. sequence of tones.  Its arguments are identical to those of the fg_sounds
  5963. routine, but the structure of the sound array is different.  Its structure
  5964. is:
  5965.                                               Chapter 13:  Sound Effects   225
  5966.  
  5967.  
  5968.                           [0]    channel # of sound 1
  5969.  
  5970.                           [1]    frequency of sound 1
  5971.  
  5972.                           [2]    volume    of sound 1
  5973.  
  5974.                           [3]    duration  of sound 1
  5975.                            .
  5976.                            .
  5977.                            .
  5978.  
  5979.                        [4n-4]    channel # of sound n
  5980.  
  5981.                        [4n-3]    frequency of sound n
  5982.  
  5983.                        [4n-2]    volume    of sound n
  5984.  
  5985.                        [4n-1]    duration  of sound n
  5986.  
  5987.                          [4n]       terminator (0)
  5988.  
  5989.  
  5990. The channel numbers, frequencies, volumes, and durations must be in the same
  5991. ranges as discussed in the description of the fg_voice routine, except the
  5992. durations are quadrupled because of the accelerated clock tick interrupt
  5993. rate.  Again, note that a null character (that is, a zero byte) terminates
  5994. the array.
  5995.  
  5996.      Example 13-5 uses the fg_voices routine to play the 500 Hertz tone
  5997. sequence of increasing volume introduced in example 13-2.  As in example
  5998. 13-4, the program displays messages while the tone sequence is playing to
  5999. demonstrate the sounds are being made concurrently with other operations.
  6000. Note how the duration is now 16 clock ticks (instead of 4 as in example 13-2)
  6001. because of the quadrupled clock tick interrupt rate.
  6002.  
  6003.                                 Example 13-5.
  6004.  
  6005.            main()
  6006.            {
  6007.               int voice_array[61];
  6008.               int i;
  6009.               int volume;
  6010.  
  6011.               if (fg_testmode(9,0) == 0) {
  6012.                  printf("This program requires a PCjr or ");
  6013.                  printf("a Tandy 1000 system.\n");
  6014.                  exit();
  6015.                  }
  6016.  
  6017.               i = 0;
  6018.  
  6019.               for (volume = 1; volume <= 15; volume++) {
  6020.                  voice_array[i++] = 1;      /* use channel 1 */
  6021.                  voice_array[i++] = 500;    /* 500 Hz frequency */
  6022.                  voice_array[i++] = volume; /* variable volume */
  6023.  
  6024. 226   Fastgraph User's Guide
  6025.  
  6026.                  voice_array[i++] = 16;     /* duration */
  6027.                  }
  6028.               voice_array[i] = 0;
  6029.  
  6030.               fg_voices(voice_array,1);
  6031.  
  6032.               while(fg_playing())
  6033.                  printf("Still playing...\n");
  6034.            }
  6035.  
  6036.  
  6037.      There is also an asynchronous version of the fg_music routine.  It is
  6038. called fg_musicb, and it uses the same format music string as the fg_music
  6039. routine does.  However, the fg_musicb routine has a second argument that
  6040. specifies the number of times to cycle through the music string.  If this
  6041. value is negative, the music will play repetitively until you stop it with
  6042. the fg_hush or fg_hushnext routine.
  6043.  
  6044.      Example 13-6 plays the same three pieces of music as example 13-3, but
  6045. it does so concurrently with other operations.  As the music plays, the
  6046. program continuously displays the title of each piece.  Note how we can take
  6047. advantage of the repetition in the music string for the "up the scale"
  6048. sequence by playing the sequence twice.
  6049.  
  6050.                                 Example 13-6.
  6051.  
  6052.    main()
  6053.    {
  6054.       fg_musicb("T150 L8 EDCDEEE P DDD P EGG P EDCDEEE L16 P L8 EDDEDC$",1);
  6055.       while (fg_playing())
  6056.          printf("Mary Had a Little Lamb...\n");
  6057.       fg_waitfor(18);
  6058.  
  6059.       fg_musicb("L16 CC#DD#EFF#GG#AA#B O+$",2);
  6060.       while (fg_playing())
  6061.          printf("up the scale in two octaves...\n");
  6062.       fg_waitfor(18);
  6063.  
  6064.       fg_musicb("T180 O2 L2 P L8 P GGG L2 D# L24 P L8 P FFF L2 D$",1);
  6065.       while (fg_playing())
  6066.          printf("Beethoven's Fifth Symphony...\n");
  6067.    }
  6068.  
  6069.                                               Chapter 13:  Sound Effects   227
  6070.  
  6071.      Our final example pertaining to asynchronous sound demonstrates the
  6072. effects of the Fastgraph routines fg_hush and fg_hushnext, which stop sounds
  6073. started with the fg_sounds, fg_voices, or fg_musicb routines.  The fg_hush
  6074. routine immediately stops asynchronous sound, whereas the fg_hushnext routine
  6075. does so when the current cycle finishes.  Neither routine has any arguments,
  6076. and neither routine has any effect if no asynchronous sound is in progress.
  6077. Furthermore, note that fg_hushnext has no effect unless the asynchronous
  6078. sound is continuous.
  6079.  
  6080.      Example 13-7 runs in any text or graphics video mode.  It displays
  6081. rectangles in up to 16 colors while playing continuous asynchronous music.
  6082. The program periodically checks for keystrokes with the fg_intkey routine,
  6083. and it continues to play the music as long as there is no keyboard activity.
  6084. If you press the Escape key, the program uses fg_hush to stop the music
  6085. immediately; this causes an exit from the while loop.  If you press any other
  6086. key, the program uses fg_hushnext to stop the music as soon as the current
  6087. repetition finishes.  Once it does, the program exits the while loop because
  6088. fg_playing will return a value of zero.
  6089.  
  6090.                                 Example 13-7.
  6091.  
  6092.       #define ESC 27
  6093.  
  6094.       main()
  6095.       {
  6096.          int color;
  6097.          int old_mode;
  6098.          unsigned char key, aux;
  6099.  
  6100.          old_mode = fg_getmode();
  6101.          fg_setmode(fg_automode());
  6102.          color = 0;
  6103.  
  6104.          fg_musicb("O4 L16 CC#DD#EFF#GG#AA#B O+ CC#DD#EFF#GG#AA#B$",-1);
  6105.  
  6106.          while (fg_playing())
  6107.          {
  6108.             color = (color + 1) & 15;
  6109.             fg_setcolor(color);
  6110.             fg_rect(0,fg_getmaxx(),0,fg_getmaxy());
  6111.  
  6112.             fg_waitfor(4);
  6113.             fg_intkey(&key,&aux);
  6114.             if (key == ESC)
  6115.                fg_hush();
  6116.             else if (key+aux != 0)
  6117.                fg_hushnext();
  6118.          }
  6119.  
  6120.          fg_setmode(old_mode);
  6121.          fg_reset();
  6122.       }
  6123.  
  6124. 228   Fastgraph User's Guide
  6125.  
  6126.  
  6127.  
  6128.      Example 13-7 also demonstrates an important side-effect of the fg_musicb
  6129. routine when playing continuous music.  Any length, octave, silence, or tempo
  6130. values changed within the string are not reset to their original values at
  6131. the beginning of each repetition.  If we did not include the O4 command at
  6132. the beginning of the string, the later O+ command would cause the music to
  6133. play in octaves 4 and 5 during the first repetition, 5 and 6 during the
  6134. second repetition, and octave 6 for all subsequent repetitions (because you
  6135. cannot increase the octave number above 6).
  6136.  
  6137.  
  6138. Summary of Sound Routines
  6139.  
  6140.      This section summarizes the functional descriptions of the Fastgraph
  6141. routines presented in this chapter.  More detailed information about these
  6142. routines, including their arguments and return values, may be found in the
  6143. Fastgraph Reference Manual.
  6144.  
  6145.      FG_HUSH immediately stops asynchronous sound started with the fg_sounds,
  6146. fg_voices, or fg_musicb routines.
  6147.  
  6148.      FG_HUSHNEXT is similar to fg_hush, but it does not stop the asynchronous
  6149. sound until the current repetition finishes.
  6150.  
  6151.      FG_MUSIC uses the programmable timer to play a sequence of musical
  6152. tones.
  6153.  
  6154.      FG_MUSICB is the asynchronous version of the fg_music routine.  It uses
  6155. the programmable timer to play a sequence of musical tones, concurrent with
  6156. other activity.
  6157.  
  6158.      FG_PLAYING determines whether or not there is any asynchronous sound in
  6159. progress.
  6160.  
  6161.      FG_QUIET stops continuous synchronous sound started with the fg_sound or
  6162. fg_voice routines.
  6163.  
  6164.      FG_SOUND produces a tone of a specified frequency and duration using the
  6165. programmable timer.
  6166.  
  6167.      FG_SOUNDS is the asynchronous version of the fg_sound routine.  It can
  6168. play a series of tones of specified frequencies and durations, concurrent
  6169. with other activity.
  6170.  
  6171.      FG_VOICE produces a tone of a specified frequency, duration, and volume
  6172. using one of the TI sound chip's four voice channels.
  6173.  
  6174.      FG_VOICES is the asynchronous version of the fg_voice routine.  It can
  6175. play a series of tones of specified frequencies, durations, and volumes,
  6176. concurrent with other activity.
  6177.  
  6178.  
  6179. Chapter 14
  6180.  
  6181. Program Timing
  6182. 230   Fastgraph User's Guide
  6183.  
  6184.  
  6185. Overview
  6186.  
  6187.      It is occasionally necessary to delay a program's execution for a brief
  6188. period, or to determine how long it takes to execute specific sections of a
  6189. program.  Fastgraph includes routines to accomplish these tasks.  Some of
  6190. these routines are said to be real-time, which means they are independent of
  6191. a system's processor speed, while the speed of others is processor-specific.
  6192. This chapter describes both classes of timing routines, all of which are
  6193. independent of the other parts of Fastgraph.
  6194.  
  6195.  
  6196. Real-Time Routines
  6197.  
  6198.      Real-time operations center around the BIOS time-of-day clock, which is
  6199. nothing more than a counter that the system automatically increments 18.2
  6200. times per second.  This number is often called the clock tick interrupt rate
  6201. because an interrupt routine performs the incrementing.  In addition, each
  6202. increment is usually called a clock tick.
  6203.  
  6204.      The Fastgraph routine fg_waitfor delays a program's execution by the
  6205. number of clock ticks specified as its argument.  Because fg_waitfor uses
  6206. clock ticks, the actual length of the delay is the same, regardless of the
  6207. system's processor speed.  Even when Fastgraph's asynchronous sound routines
  6208. quadruple the clock tick interrupt rate, Fastgraph compensates for this
  6209. internally so that fg_waitfor always works as though the actual rate were
  6210. still 18.2 times per second.
  6211.  
  6212.      Example 14-1 displays a message every five seconds that states how long
  6213. the program has been running.  The fg_waitfor routine produces the five-
  6214. second delay by pausing 91 (18.2 times 5) clock ticks before the program
  6215. displays each message.  The program returns to DOS when you press any key.
  6216.  
  6217.                                 Example 14-1.
  6218.  
  6219.              main()
  6220.              {
  6221.                 unsigned int seconds;
  6222.                 unsigned char key, aux;
  6223.  
  6224.                 seconds = 0;
  6225.  
  6226.                 fg_intkey(&key,&aux);
  6227.  
  6228.                 while (key+aux == 0) {
  6229.                    fg_waitfor(91);
  6230.                    seconds += 5;
  6231.                    printf("%u seconds have elapsed.\n",seconds);
  6232.                    fg_intkey(&key,&aux);
  6233.                 }
  6234.              }
  6235.  
  6236.  
  6237.      Another common application of the fg_waitfor routine is to slow down a
  6238. loop that uses the fg_intkey routine to check for keystrokes.  In loops that
  6239. do little else, we may call fg_intkey too rapidly without this delay, and it
  6240. is then possible that the BIOS may not be able to store characters in its
  6241.                                              Chapter 14:  Program Timing   231
  6242.  
  6243. keystroke buffer fast enough.  A small delay, even one clock tick, often
  6244. helps such "tight" loops.
  6245.  
  6246.      The fg_getclock routine provides an efficient way to measure time,
  6247. especially differences in time.  This routine has no arguments and returns a
  6248. 32-bit unsigned integer (as its function value) representing the number of
  6249. clock ticks since midnight.
  6250.  
  6251.      Example 14-2 demonstrates the fg_getclock routine.  In response to any
  6252. keystroke (except Escape, which returns control to DOS), the program displays
  6253. the number of clock ticks since midnight, and also the number since the
  6254. program started.  The FASTGRAF.H include file is needed in this example to
  6255. define fg_getclock as a function that returns a long integer.
  6256.  
  6257.                                 Example 14-2.
  6258.  
  6259.       #include <fastgraf.h>
  6260.       #define ESC 27
  6261.  
  6262.       main()
  6263.       {
  6264.          unsigned long start, ticks;
  6265.          unsigned char key, aux;
  6266.  
  6267.          start = fg_getclock();
  6268.  
  6269.          fg_getkey(&key,&aux);
  6270.  
  6271.          while (key != ESC) {
  6272.             ticks = fg_getclock();
  6273.             printf("%lu ticks since midnight.\n",ticks);
  6274.             printf("%lu ticks since start of program.\n\n",ticks-start);
  6275.             fg_getkey(&key,&aux);
  6276.          }
  6277.       }
  6278.  
  6279.  
  6280.  
  6281.  
  6282. Routines Dependent on the System Speed
  6283.  
  6284.      The fg_waitfor routine described in the previous section is independent
  6285. of the system's processor speed.  This means the actual length of its delay
  6286. is the same on any system.  Another routine, fg_stall, is similar to
  6287. fg_waitfor, but its delay is proportional to the processor speed.  Like
  6288. fg_waitfor, fg_stall has a single integer argument that specifies the length
  6289. of the delay.  However, instead of being expressed in clock ticks, fg_stall
  6290. measures the delay in delay units.  The fg_stall routine treats the length as
  6291. an unsigned quantity, so the maximum number of delay units we can specify is
  6292. 65,535.  The following table lists the approximate number of delay units per
  6293. clock tick on three typical systems.
  6294. 232   Fastgraph User's Guide
  6295.  
  6296.  
  6297.                           system       delay units
  6298.                            type      per clock tick
  6299.  
  6300.                       Tandy 1000 HX        675
  6301.                       10 MHz 80286       3,000
  6302.                       25 MHz 80386      11,000
  6303.  
  6304.  
  6305.      Fastgraph includes a routine that determines the number of delay units
  6306. per clock tick for the current processor.  This is the fg_measure routine,
  6307. which has no arguments and returns the number of delay units per clock tick
  6308. as its function value.  Once we determine this value, we can use fg_stall to
  6309. delay a program's execution in real time.  This provides a much more refined
  6310. delay than the clock tick unit used by fg_waitfor.
  6311.  
  6312.      Example 14-3 is functionally identical to example 14-1, but it uses the
  6313. fg_stall routine instead of fg_waitfor to delay the program execution.  The
  6314. program first calls the fg_measure routine to determine number of delay units
  6315. equivalent to one clock tick.  It then passes this value to fg_stall, which
  6316. is called 91 times inside the for loop to create the five-second delay
  6317. (because 91 clock ticks equals five seconds).  The program returns to DOS
  6318. when you press any key.
  6319.  
  6320.                                 Example 14-3.
  6321.  
  6322.              main()
  6323.              {
  6324.                 int i;
  6325.                 int units_per_tick;
  6326.                 unsigned int seconds;
  6327.                 unsigned char key, aux;
  6328.  
  6329.                 seconds = 0;
  6330.  
  6331.                 printf("Benchmarking system speed...\n");
  6332.                 units_per_tick = fg_measure();
  6333.                 printf("Benchmark completed.\n\n");
  6334.  
  6335.                 fg_intkey(&key,&aux);
  6336.  
  6337.                 while (key+aux == 0) {
  6338.                    for (i = 0; i < 91; i++)
  6339.                       fg_stall(units_per_tick);
  6340.                    seconds += 5;
  6341.                    printf("%u seconds have elapsed.\n",seconds);
  6342.                    fg_intkey(&key,&aux);
  6343.                 }
  6344.              }
  6345.  
  6346.  
  6347.      One final point:  the fg_measure routine takes a few seconds to
  6348. benchmark the system speed accurately.  For this reason, you should only call
  6349. fg_measure once (typically at the beginning of the program) and use its
  6350. return value instead of calling fg_measure throughout the program.
  6351.                                              Chapter 14:  Program Timing   233
  6352.  
  6353.  
  6354. Summary of Timing Routines
  6355.  
  6356.      This section summarizes the functional descriptions of the Fastgraph
  6357. routines presented in this chapter.  More detailed information about these
  6358. routines, including their arguments and return values, may be found in the
  6359. Fastgraph Reference Manual.
  6360.  
  6361.      FG_GETCLOCK returns the number of clock ticks since midnight as its
  6362. function value.  This quantity is a 32-bit unsigned integer.
  6363.  
  6364.      FG_MEASURE returns the approximate number of delay units per clock tick
  6365. as its function value.  This quantity is proportional to the system's
  6366. processor speed.
  6367.  
  6368.      FG_STALL delays a program's execution for a given number of processor-
  6369. specific delay units.
  6370.  
  6371.      FG_WAITFOR delays a program's execution for a given number of clock
  6372. ticks.  There are 18.2 clock ticks per second, regardless of the system's
  6373. processor speed.
  6374. 234   Fastgraph User's Guide
  6375.  
  6376.  
  6377. Chapter 15
  6378.  
  6379. Miscellaneous Routines
  6380. 236   Fastgraph User's Guide
  6381.  
  6382.  
  6383. Overview
  6384.  
  6385.      There are a few remaining Fastgraph routines that really don't fit into
  6386. any of the categories discussed so far.  For this reason, they are described
  6387. separately in this chapter.
  6388.  
  6389.  
  6390. Determining Available Memory
  6391.  
  6392.      The fg_memavail routine returns the amount of free memory (in bytes)
  6393. available to DOS.  It returns the amount of memory as its function value,
  6394. which is a 32-bit unsigned integer.  Fg_memavail has no arguments.
  6395.  
  6396.      Example 15-1 uses fg_memavail to show the effects of allocating and
  6397. releasing virtual pages.  When run in a video mode in which video pages 1 and
  6398. 2 are physical pages, the amount of free memory remains the same because
  6399. these pages use memory that is resident on the video adapter.  However, in
  6400. modes where pages 1 and 2 are virtual pages, the amount of free memory
  6401. decreases after each call to fg_allocate and returns to its original value
  6402. after the calls to fg_freepage.  Note how the program requests the video
  6403. mode, and that we must include the FASTGRAF.H file to define fg_memavail as a
  6404. function that returns a long integer.
  6405.  
  6406.                                 Example 15-1.
  6407.  
  6408.        #include <fastgraf.h>
  6409.  
  6410.        main()
  6411.        {
  6412.           long original, mem0, mem1, mem2;
  6413.           int  mode, old_mode;
  6414.  
  6415.           printf("Which video mode? ");
  6416.           scanf("%d",&mode);
  6417.  
  6418.           if (fg_testmode(mode,0) == 0) {
  6419.              printf("Your system does not support that video mode.\n");
  6420.              exit();
  6421.              }
  6422.           if (fg_testmode(mode,3) == 0) {
  6423.              printf("Your system does not have enough memory.\n");
  6424.              exit();
  6425.              }
  6426.  
  6427.           original = fg_memavail();
  6428.           old_mode = fg_getmode();
  6429.           fg_setmode(mode);
  6430.           mem0 = fg_memavail();
  6431.           fg_allocate(1);
  6432.           mem1 = fg_memavail();
  6433.           fg_allocate(2);
  6434.           mem2 = fg_memavail();
  6435.  
  6436.           fg_freepage(1);
  6437.           fg_freepage(2);
  6438.  
  6439.                                      Chapter 15:  Miscellaneous Routines   237
  6440.  
  6441.           fg_setmode(old_mode);
  6442.           fg_reset();
  6443.  
  6444.           printf("originally     = %ld\n",original);
  6445.           printf("after setmode  = %ld\n",mem0);
  6446.           printf("after 1st page = %ld\n",mem1);
  6447.           printf("after 2nd page = %ld\n",mem2);
  6448.           printf("at end         = %ld\n",memavail());
  6449.        }
  6450.  
  6451.  
  6452.  
  6453. Choosing the Video Memory Update Function
  6454.  
  6455.      In chapter 10, we saw how to use the fg_setfunc routine to perform XOR
  6456. animation in native EGA and VGA graphics modes (modes 13 to 18).  In these
  6457. video modes, fg_setfunc controls the logical operation applied when the
  6458. contents of video memory change.  The specific operation is defined by its
  6459. argument, as shown below.
  6460.  
  6461.                          value of  logical
  6462.                          argument  operation
  6463.  
  6464.                              0     replacement
  6465.                              1     and
  6466.                              2     or
  6467.                              3     exclusive or
  6468.  
  6469. If your program does not use the fg_setfunc routine, replacement mode is
  6470. always used.  That is, information written to video memory replaces whatever
  6471. was there before.  The fg_setfunc routine does nothing in CGA, Tandy/PCjr,
  6472. Hercules, or MCGA graphics modes, or in any text modes.
  6473.  
  6474.      Example 15-2 demonstrates the fg_setfunc routine.  The program is
  6475. similar to example 6-10 which displays 200 random rectangles on the screen.
  6476. However, example 15-2 displays the rectangles in XOR mode, which means the
  6477. rectangle intersections will appear in different colors.
  6478.  
  6479.                                 Example 15-2.
  6480.  
  6481.             #define RECTANGLES 200
  6482.             #define SWAP(a,b,temp) { temp = a; a = b; b = temp; }
  6483.  
  6484.             main()
  6485.             {
  6486.                int i;
  6487.                int minx, maxx, miny, maxy;
  6488.                int old_mode;
  6489.                int temp;
  6490.                int xres, yres;
  6491.  
  6492.                if (fg_egacheck() == 0) {
  6493.                   printf("This program requires EGA or VGA.\n");
  6494.                   exit();
  6495.                   }
  6496.  
  6497. 238   Fastgraph User's Guide
  6498.  
  6499.  
  6500.                old_mode = fg_getmode();
  6501.                fg_setmode(fg_automode());
  6502.                fg_setfunc(3);
  6503.  
  6504.                xres = fg_getmaxx() + 1;
  6505.                yres = fg_getmaxy() + 1;
  6506.  
  6507.                for (i = 0; i < RECTANGLES; i++) {
  6508.                   minx = rand() % xres;
  6509.                   maxx = rand() % xres;
  6510.                   miny = rand() % yres;
  6511.                   maxy = rand() % yres;
  6512.                   if (minx > maxx)
  6513.                      SWAP(minx,maxx,temp);
  6514.                   if (miny > maxy)
  6515.                      SWAP(miny,maxy,temp);
  6516.                   fg_setcolor(rand()%16);
  6517.                   fg_rect(minx,maxx,miny,maxy);
  6518.                   }
  6519.  
  6520.                fg_setmode(old_mode);
  6521.                fg_reset();
  6522.             }
  6523.  
  6524.  
  6525.  
  6526. Summary of Miscellaneous Routines
  6527.  
  6528.      This section summarizes the functional descriptions of the Fastgraph
  6529. routines presented in this chapter.  More detailed information about these
  6530. routines, including their arguments and return values, may be found in the
  6531. Fastgraph Reference Manual.
  6532.  
  6533.      FG_MEMAVAIL returns the amount of memory available to DOS.
  6534.  
  6535.      FG_SETFUNC specifies the logical operation (replacement, or, and,
  6536. exclusive or) applied when video memory changes in the native EGA and VGA
  6537. graphics modes.  This routine has no effect in other video modes.
  6538.  
  6539.  
  6540. Appendix A
  6541.  
  6542. Fastgraph Utilities
  6543. 240   Fastgraph User's Guide
  6544.  
  6545.  
  6546. Overview
  6547.  
  6548.      This appendix describes three utilities that allow you to create and
  6549. manage image files used with the fg_dispfile routine.  The Fastgraph
  6550. installation procedure places these utilities in the \FG directory.  To use
  6551. these utilities, you must either (1) copy the appropriate .EXE file from \FG
  6552. to your current directory, (2) make \FG your current directory, or (3)
  6553. include the \FG directory in your DOS path specification.
  6554.  
  6555.  
  6556. SNAPSHOT Utility
  6557.  
  6558.      The SNAPSHOT utility is a terminate and stay resident program (TSR) to
  6559. capture graphic images.  It stores the image in Fastgraph's standard pixel
  6560. run format.
  6561.  
  6562.      To load SNAPSHOT, just enter the command SNAPSHOT at the DOS prompt, and
  6563. you'll see messages similar to the following if SNAPSHOT loads successfully.
  6564.  
  6565.  
  6566.         C> SNAPSHOT
  6567.  
  6568.         SNAPSHOT  Version 1.0
  6569.         Copyright (c) 1991 Ted Gruber Software.  All Rights Reserved.
  6570.  
  6571.         Press <alt>-<left shift> to activate.
  6572.  
  6573.  
  6574. After SNAPSHOT loads, control returns to the DOS prompt.  At this point, you
  6575. can use any method whatsoever to display a graphic image and then press the
  6576. Alt and left shift keys at the same time to capture the image.  You don't
  6577. need to load SNAPSHOT for each image capture, just once per system boot.
  6578. SNAPSHOT uses about 12,000 bytes of conventional memory once loaded.
  6579.  
  6580.      To illustrate the use of SNAPSHOT, suppose you have drawn and saved an
  6581. image with a commercial paint program, and you want to incorporate this image
  6582. into a Fastgraph application.  Once you load SNAPSHOT, activate the paint
  6583. program and retrieve your image.  Then press the Alt and left shift keys
  6584. simultaneously and wait for the success tone (three quick medium-pitched
  6585. sounds).  Finally, exit the paint program to return to the DOS prompt.
  6586.  
  6587.      The sequence described in the preceding paragraph will store the
  6588. captured image in Fastgraph's standard pixel run format, in a file named
  6589. SNAPSHOT.nnn in the current directory.  The file type nnn will be the first
  6590. sequence of digits that does not result in a duplicate file name.  That is,
  6591. if there are no captured image files in the current directory, SNAPSHOT will
  6592. use the file name SNAPSHOT.000.  The next time you capture an image, SNAPSHOT
  6593. will store it in SNAPSHOT.001, then SNAPSHOT.002, and so forth.  If you
  6594. rename or delete one of these files, SNAPSHOT will again use that file name.
  6595. For example, if you delete SNAPSHOT.000 but keep SNAPSHOT.001, SNAPSHOT will
  6596. store the next image it captures in SNAPSHOT.000.
  6597.  
  6598.      If for some reason SNAPSHOT is unable to capture the image, it will
  6599. produce its error tone (a single low-pitched sound).  The most common cause
  6600. of this is trying to capture an image from a text video mode, but it will
  6601.                                         Appendix A:  Fastgraph Utilities   241
  6602.  
  6603. also occur if there is insufficient disk space or if all 1,000 image file
  6604. names are already being used.
  6605.  
  6606.  
  6607. CLIP Utility
  6608.  
  6609.      The SNAPSHOT utility described in the previous section captures the
  6610. entire screen.  While this might be desirable in many cases, other times
  6611. you'll just need a portion of the screen.  CLIP is an interactive utility
  6612. that you can use to reduce the size of any image stored in Fastgraph's
  6613. standard or packed pixel run format.
  6614.  
  6615.      The syntax of the command for invoking the CLIP utility from the DOS
  6616. prompt is
  6617.  
  6618.                      CLIP input_file output_file options
  6619.  
  6620. where input_file is the name of the original image file, and output_file is
  6621. the name of the new image file.  CLIP does not modify the input_file in any
  6622. way, but it will overwrite the output_file if an identically named file
  6623. exists in the current directory.  The options list specifies one or more
  6624. optional switches as shown below.
  6625.  
  6626.  
  6627.                option         meaning
  6628.  
  6629.                /M:mode   Specifies the video mode number in which to
  6630.                          display the image.  The mode value must be an
  6631.                          integer between 0 and 21.  If that video mode
  6632.                          is a text mode, an unsupported graphics mode,
  6633.                          or an unavailable graphics mode, CLIP
  6634.                          displays an error message stating this.  If
  6635.                          the /M switch is not present, CLIP uses the
  6636.                          first available video mode from the list 16,
  6637.                          15, 19, 13, 9, 4, 12.
  6638.  
  6639.                /P        Indicates the input_file is in Fastgraph's
  6640.                          packed pixel run format.  If the /P switch is
  6641.                          not present, CLIP assumes it is in standard
  6642.                          pixel run format.  The output_file will be in
  6643.                          the same format as the input_file.
  6644.  
  6645.                /W:width  Specifies the image width in pixels.  The
  6646.                          width value must be an integer between 1 and
  6647.                          the horizontal resolution of the selected
  6648.                          video mode.  If the /W switch is not present,
  6649.                          CLIP uses the horizontal resolution of the
  6650.                          selected video mode.
  6651.  
  6652.  
  6653. For example, if you wanted to create the image file PARTIAL.PPR from the
  6654. packed pixel run file SCREEN.PPR, and use the native 320 by 200 EGA graphics
  6655. video mode (mode 13), you would activate CLIP with the following command.
  6656.  
  6657.  
  6658.                      CLIP PARTIAL.PPR SCREEN.PPR /P /M:13
  6659.  
  6660. 242   Fastgraph User's Guide
  6661.  
  6662.  
  6663.  
  6664. Because no /W switch appears in the above command and the horizontal
  6665. resolution of mode 13 is 320 pixels, CLIP assumes the image width is 320
  6666. pixels.
  6667.  
  6668.      When CLIP displays the image and the plus-shaped cursor, you are ready
  6669. to define one corner of the clipping region (that part of the image used to
  6670. create the output_file).  To do this, use the directional keys on the numeric
  6671. keypad to move the cursor to the desired position, then press the Enter key.
  6672. You are then ready to define the clipping region's opposite corner.  Again,
  6673. use the directional keys to move the cursor to the desired position.  When
  6674. defining the second corner, however, CLIP uses a rectangular box instead of
  6675. the plus-shaped cursor to simplify marking the clipping region's boundaries.
  6676. After you press Enter to define the second corner, CLIP creates the
  6677. output_file and displays the resulting image width and the number of pixel
  6678. runs the image contains.
  6679.  
  6680.      CLIP includes some features that may help you define the clipping
  6681. region.  You can change the distance the cursor moves in response to the
  6682. directional keys, display the current (x,y) pixel coordinates of the cursor,
  6683. and change the cursor color.  The following table explains the keystrokes
  6684. that CLIP recognizes when you are defining the clipping region.
  6685.  
  6686.  
  6687.        key       meaning
  6688.  
  6689.        F1        Displays the (x,y) coordinate bar at the top
  6690.                  of the screen.  If the coordinate bar is
  6691.                  already on, F1 removes it.
  6692.        F2        Displays the (x,y) coordinate bar at the
  6693.                  bottom of the screen.  If the coordinate bar
  6694.                  is already on, F2 removes it.
  6695.        F3        Changes the cursor or box color from white to
  6696.                  black, or from black to white.
  6697.        F4        Displays a summary of the keys CLIP recognizes
  6698.                  when defining the clipping region.
  6699.        KP1       Moves the cursor one unit down and to the left.
  6700.        KP2       Moves the cursor one unit down.
  6701.        KP3       Moves the cursor one unit down and to the right.
  6702.        KP4       Moves the cursor one unit to the left.
  6703.        KP6       Moves the cursor one unit to the right.
  6704.        KP7       Moves the cursor one unit up and to the left.
  6705.        KP8       Moves the cursor one unit up.
  6706.        KP9       Moves the cursor one unit up and to the right.
  6707.        +         Increases the unit of cursor movement by one
  6708.                  pixel.  The default cursor movement is one pixel.
  6709.        -         Decreases the unit of cursor movement by one pixel.
  6710.        Enter     Defines a corner of the clipping region at the
  6711.                  cursor position.
  6712.        Esc       Exits to DOS without creating the output_file.
  6713.                  CLIP will first issue an "Exit to DOS?" prompt in
  6714.                  case you pressed the Esc key accidentally.
  6715.  
  6716.                                         Appendix A:  Fastgraph Utilities   243
  6717.  
  6718.      The CLIP utility requires two video pages to run.  Thus, you cannot use
  6719. it in video modes 17 and 18, which have only one physical video page and no
  6720. virtual video pages.
  6721.  
  6722.  
  6723. CONVERT Utility
  6724.  
  6725.      The CONVERT utility lets you translate files between Fastgraph's
  6726. supported image file formats.  The syntax of the command for invoking CONVERT
  6727. from the DOS prompt is
  6728.  
  6729.  
  6730.                         CONVERT input_file output_file
  6731.  
  6732.  
  6733. where input_file is the name of the original image file, and output_file is
  6734. the name of the new translated image file.  CONVERT does not modify the
  6735. input_file in any way, but it will overwrite the output_file if an
  6736. identically named file exists in the current directory.
  6737.  
  6738.      By default, the file type of the input_file and output_file determine
  6739. the image format of that file.  If the file type is .PPR, CONVERT assumes the
  6740. image is in Fastgraph's packed pixel run format.  If the file type is .SPR,
  6741. CONVERT assumes it is in the Fastgraph's standard pixel run format.  If your
  6742. image files use other file types, you can explicitly specify the file's image
  6743. format by appending one of the switches /PPR or /SPR to the file name.  The
  6744. input_file and output_file must not both specify the same image format
  6745. (CONVERT will display an error message if this is so).
  6746.  
  6747.      The following command will translate the standard pixel run file
  6748. PICTURE.SPR to packed format.  The packed image will be stored in the file
  6749. PICTURE.IMG, so we must append the switch /PPR to tell CONVERT that it will
  6750. be a packed file.
  6751.  
  6752.  
  6753.                      CONVERT PICTURE.SPR PICTURE.IMG/PPR
  6754.  
  6755. 244   Fastgraph User's Guide
  6756.  
  6757.  
  6758. Appendix B
  6759.  
  6760. Using Fastgraph from Assembly Language
  6761. 246   Fastgraph User's Guide
  6762.  
  6763.  
  6764.      Fastgraph uses the same naming and calling conventions as Microsoft C
  6765. and Turbo C.  The details of these conventions that are important to assembly
  6766. language programming are summarized below.  If you are calling Fastgraph
  6767. routines from an assembly language program, the program must follow these
  6768. conventions.
  6769.  
  6770.        All arrays and pointers are passed by reference
  6771.        All other items are passed by value
  6772.        Arguments are pushed onto the stack in reverse order
  6773.        16-bit function values are returned in the AX register
  6774.        32-bit function values are returned in the DX:AX register pair
  6775.        Fastgraph routine names are prefixed with an underscore character
  6776.  
  6777. The small and medium model Fastgraph libraries pass arrays and pointers by
  6778. near reference, while the large model library does so by far reference.  This
  6779. is consistent with the run-time libraries for the supported compilers.
  6780.  
  6781.      All Fastgraph routines preserve the BP, DS, DI, and SI registers.  The
  6782. contents of any other registers are unknown upon return from a Fastgraph
  6783. routine (except for the AX register, which will either contain zero or the
  6784. routine's return value).
  6785.  
  6786.      The following DOS commands show how to assemble a program (using the
  6787. Microsoft Macro Assembler) and then link it with Fastgraph.  In all cases,
  6788. we'll assume the file EXAMPLE.ASM contains the source code for the program.
  6789. The resulting executable file will be called EXAMPLE.EXE.
  6790.  
  6791.                               small memory model
  6792.  
  6793.                          MASM EXAMPLE.ASM;
  6794.                          LINK /CP:4096 /E EXAMPLE,,NUL.MAP,FGS
  6795.  
  6796.  
  6797.                              medium memory model
  6798.  
  6799.                          MASM EXAMPLE.ASM;
  6800.                          LINK /CP:4096 /E EXAMPLE,,NUL.MAP,FGM
  6801.  
  6802.  
  6803.                               large memory model
  6804.  
  6805.                          MASM EXAMPLE.ASM;
  6806.                          LINK /CP:4096 /E EXAMPLE,,NUL.MAP,FGL
  6807.  
  6808.  
  6809.      Example B-1 calls the fg_getmode, fg_setmode, fg_reset, and fg_version
  6810. routines from an assembly language program.  The fg_getmode routine returns
  6811. its function value in the AX register.  The fg_setmode routine has a single
  6812. argument, while fg_reset has no arguments.  The fg_version routine has two
  6813. arguments, both passed by reference.  Notice how they are pushed on the stack
  6814. in reverse order.  This particular example would work with either the medium
  6815. or large memory model Fastgraph libraries.  To make it work with the small
  6816. model library, all you would need to do is change the word "far" to "near" in
  6817. the EXTRN declarations, and also change the name of the code segment from
  6818. "main_TEXT" to "_TEXT".
  6819.                      Appendix B:  Using Fastgraph from Assembly Language   247
  6820.  
  6821.                                  Example B-1.
  6822.  
  6823.                 EXTRN   _fg_getmode:far  ; Fastgraph's GETMODE routine
  6824.                 EXTRN   _fg_reset:far    ; Fastgraph's RESET routine
  6825.                 EXTRN   _fg_setmode:far  ; Fastgraph's SETMODE routine
  6826.                 EXTRN   _fg_version:far  ; Fastgraph's VERSION routine
  6827.  
  6828.       stackseg  SEGMENT stack         ; suppress the linker's
  6829.       stackseg  ENDS                  ; "no stack segment" error message
  6830.  
  6831.       _DATA     SEGMENT word public 'DATA'
  6832.  
  6833.       major     dw      ?             ; major version number
  6834.       minor     dw      ?             ; minor version number
  6835.       old_mode  dw      ?             ; original video mode
  6836.  
  6837.       _DATA     ENDS
  6838.  
  6839.       dgroup    GROUP   _DATA
  6840.                 ASSUME  cs:main_TEXT,ds:dgroup
  6841.  
  6842.       main_TEXT SEGMENT byte public 'CODE'
  6843.  
  6844.       start:    mov     ax,_DATA      ; load segment location
  6845.                 mov     ds,ax         ; into DS register
  6846.  
  6847.                 call    _fg_getmode   ; AX = current video mode
  6848.                 mov     old_mode,ax   ; save it
  6849.  
  6850.                 mov     ax,4          ; use video mode 4
  6851.                 push    ax            ; pass argument to SETMODE
  6852.                 call    _fg_setmode   ; establish CGA four-color mode
  6853.                 add     sp,2          ; remove SETMODE argument
  6854.  
  6855.                 push    old_mode      ; pass argument to SETMODE
  6856.                 call    _fg_setmode   ; restore original video mode
  6857.                 add     sp,2          ; remove SETMODE argument
  6858.  
  6859.                 call    _fg_reset     ; restore screen attributes
  6860.  
  6861.                 lea     ax,minor      ; get address of minor variable
  6862.                 push    ax            ; pass argument #2 to VERSION
  6863.                 lea     ax,major      ; get address of major variable
  6864.                 push    ax            ; pass argument #1 to VERSION
  6865.                 call    _fg_version   ; get the Fastgraph version number
  6866.                 add     sp,4          ; remove VERSION arguments
  6867.  
  6868.                 mov     ah,76         ; function 76: terminate process
  6869.                 xor     al,al         ; errorlevel 0
  6870.                 int     21h           ; exit to DOS
  6871.  
  6872.       main_TEXT ENDS
  6873.                 END     start
  6874.  
  6875. 248   Fastgraph User's Guide
  6876.  
  6877.  
  6878. Appendix C
  6879.  
  6880. Interrupts and Fastgraph
  6881. 250   Fastgraph User's Guide
  6882.  
  6883.  
  6884. Interrupts Used by Fastgraph
  6885.  
  6886.      DOS maintains an interrupt vector table that contains the addresses of
  6887. 256 interrupt handlers, or routines, that perform various functions.  The
  6888. handlers are usually referenced by their hexadecimal interrupt number,
  6889. between 00 and FF.  Of these, only interrupts 60 through 67 and F1 through FF
  6890. are not used by DOS or the ROM BIOS and are thus available for user
  6891. applications.
  6892.  
  6893.      Certain Fastgraph routines use some of the available interrupts.
  6894. Namely, the fg_music routine uses interrupt 60, the asynchronous sound
  6895. routines (fg_musicb, fg_sounds, and fg_voices) use interrupts 60 and 61, all
  6896. Fastgraph/Light routines use interrupt 62, and any program that runs in the
  6897. native EGA or VGA graphics video modes (modes 13 through 18) uses interrupt
  6898. 64.  If your program defines its own interrupt handlers, it must not use any
  6899. of the interrupts reserved for Fastgraph (unless, of course, it doesn't use
  6900. any of the Fastgraph routines or video modes that would create a conflict).
  6901.  
  6902.  
  6903. Extending the Time-of-Day Interrupt
  6904.  
  6905.      As mentioned in chapter 14, the BIOS time-of-day clock is incremented by
  6906. an interrupt handler.  The routine that does this is interrupt 08, a hardware
  6907. interrupt automatically activated 18.2 times per second.  After incrementing
  6908. the clock, interrupt 08 invokes interrupt 1C, which by default references a
  6909. "do-nothing" interrupt handler.  While modifying interrupt 08 can be tricky,
  6910. it is fairly straightforward to define our own handler for interrupt 1C.
  6911. This handler will also be executed automatically 18.2 times per second.
  6912. Example C-1 illustrates how to do this.
  6913.  
  6914.      When we discussed joysticks in chapter 12, we said there were two ways
  6915. to monitor joystick button status.  One is to intersperse calls to the
  6916. fg_button routine at strategic places in your program and then take
  6917. appropriate action depending on the button status.  However, the problem with
  6918. this scheme is the possibility of missing a button press -- if you press the
  6919. joystick button and then release it between calls to fg_button, the program
  6920. will not detect the joystick activity.  A preferable method is to call
  6921. fg_button from a handler for interrupt 1C, which essentially provides
  6922. continuous monitoring of the joystick buttons.  When we need the button
  6923. status within our program, all we need to do is examine a global variable.
  6924.  
  6925.      Example C-1 consists of a main program (written in C) and an assembly
  6926. language subroutine named int1C (suitable for the medium memory model).  The
  6927. main program calls int1C to define a handler for interrupt 1C.  In response
  6928. to any keystroke (except Escape), the program displays the button press
  6929. information for each joystick since the previous keystroke (refer to the
  6930. discussion of the fg_button routine for the meanings of the status values).
  6931. When you press the Escape key, the program exits to DOS, but not before
  6932. calling int1C to restore the original interrupt 1C handler.
  6933.  
  6934.                                    Appendix C:  Interrupts and Fastgraph   251
  6935.  
  6936.                          Example C-1 (main program).
  6937.  
  6938.                #define ESC 27
  6939.  
  6940.                int status1, status2;
  6941.                main()
  6942.                {
  6943.                   unsigned char key, aux;
  6944.  
  6945.                   int1C(1);
  6946.  
  6947.                   status1 = 0;
  6948.                   status2 = 0;
  6949.                   fg_getkey(&key,&aux);
  6950.  
  6951.                   while (key != ESC) {
  6952.                      printf("\n");
  6953.                      printf("Joystick 1 status: %d\n",status1);
  6954.                      printf("Joystick 2 status: %d\n",status2);
  6955.                      status1 = 0;
  6956.                      status2 = 0;
  6957.                      fg_getkey(&key,&aux);
  6958.                      }
  6959.  
  6960.                   int1C(0);
  6961.                }
  6962.  
  6963.  
  6964.      We'll now examine the int1C assembly language subroutine.  It actually
  6965. consists of three parts:  a portion to enable our interrupt handler, our
  6966. handler itself, and a portion to disable the handler.  When we call int1C
  6967. with a nonzero argument, it saves the original data segment (so we can access
  6968. the global variables within the handler), saves the original handler's
  6969. address (called the vector) for interrupt 1C, and then enables our handler,
  6970. which takes the form of a far procedure.
  6971.  
  6972.      The handler routine then begins to be activated at 18.2 times per
  6973. second.  After saving all of the important registers, the handler calls the
  6974. Fastgraph routine fg_button twice, once for each joystick.  The return values
  6975. are logically ORed with the status1 and status2 C global variables to update
  6976. the button status information.  Finally, the handler restores the original
  6977. registers and returns control to the point of the interrupt.
  6978.  
  6979.      Before the main program exits, it calls int1C with a zero argument to
  6980. restore the original handler for interrupt 1C.  No provision is made in the
  6981. program to check if we had previously defined our own handler (and hence
  6982. saved the original interrupt 1C vector), but this could be added with little
  6983. difficulty.
  6984.  
  6985.                  Example C-1 (assembly language subroutine).
  6986.  
  6987.               EXTRN   _status1:word ; C global variable for button 1 status
  6988.               EXTRN   _status2:word ; C global variable for button 2 status
  6989.               EXTRN   _fg_button:far ; Fastgraph routine
  6990.  
  6991.    int1C_TEXT SEGMENT byte public 'CODE'
  6992.  
  6993. 252   Fastgraph User's Guide
  6994.  
  6995.               ASSUME  cs:int1C_TEXT
  6996.  
  6997.    int1C_CS   dw      ?             ; holds original INT 1C segment address
  6998.    int1C_IP   dw      ?             ; holds original INT 1C offset
  6999.    orig_DS    dw      ?             ; holds original data segment
  7000.  
  7001.    _int1C     PROC    far
  7002.               PUBLIC  _int1C
  7003.  
  7004.               push    bp            ; save caller's BP register
  7005.               mov     bp,sp         ; make BP point to argument list
  7006.               push    si            ; save caller's SI register
  7007.               push    di            ; save caller's DI register
  7008.  
  7009.               mov     dx,[bp+6]     ; get the flag parameter
  7010.               or      dx,dx         ; replace the old interrupt handler?
  7011.               jz      replace       ; yes, branch to that processing
  7012.  
  7013.    ; define a new handler for INT 1C
  7014.  
  7015.    define:    mov     ax,ds         ; put current data segment in AX
  7016.               mov     cs:orig_DS,ax ; save it in the control information area
  7017.               mov     al,1Ch        ; interrupt vector to save
  7018.               mov     ah,53         ; function 53: get interrupt vector
  7019.               int     21h           ; get the interrupt vector
  7020.               mov     cs:int1C_CS,es; save the segment
  7021.               mov     cs:int1C_IP,bx; save the offset
  7022.  
  7023.               push    ds            ; save our DS register
  7024.               mov     dx,offset handler ; get offset of interrupt handler
  7025.               mov     ax,seg handler; get segment of interrupt handler
  7026.               mov     ds,ax         ; put it in DS
  7027.               mov     al,1Ch        ; interrupt vector to change
  7028.               mov     ah,37         ; function 37: set interrupt vector
  7029.               int     21h           ; change the INT 1C vector to our handler
  7030.               pop     ds            ; restore our DS register
  7031.               jmp     short return  ; return to the caller
  7032.  
  7033.    ; replace the original handler for INT 1C
  7034.  
  7035.    replace:   push    ds            ; save our DS register
  7036.               mov     dx,cs:int1C_IP; put original INT 1C offset in DX
  7037.               mov     ds,cs:int1C_CS; put original INT 1C segment in DS
  7038.               mov     ah,37         ; function 37: set interrupt vector
  7039.               mov     al,1Ch        ; interrupt vector 1C
  7040.               int     21h           ; restore original INT 1C vector
  7041.               pop     ds            ; restore our DS register
  7042.  
  7043.    return:    xor     ax,ax         ; in case int1C was called as a function
  7044.               pop     di            ; restore our DI register
  7045.               pop     si            ; restore our SI register
  7046.               pop     bp            ; restore our BP register
  7047.               ret
  7048.  
  7049.                                    Appendix C:  Interrupts and Fastgraph   253
  7050.  
  7051.    _int1C     ENDP
  7052.  
  7053.    handler    PROC    far           ; interrupt handler that replaces INT 1C
  7054.  
  7055.               cli                   ; disable interrupts while handler active
  7056.               push    ax            ; save registers that may be altered
  7057.               push    bx
  7058.               push    cx
  7059.               push    dx
  7060.               push    di
  7061.               push    si
  7062.               push    ds
  7063.               push    es
  7064.  
  7065.               mov     ds,cs:orig_DS ; retrieve the original data segment
  7066.  
  7067.               mov     ax,1          ; use joystick 1
  7068.               push    ax            ; pass joystick number to button routine
  7069.               call    _fg_button    ; AX = button status for joystick 1
  7070.               add     sp,2          ; remove the argument
  7071.               or      _status1,ax   ; update status variable for joystick 1
  7072.  
  7073.               mov     ax,2          ; use joystick 2
  7074.               push    ax            ; pass joystick number to button routine
  7075.               call    _fg_button    ; AX = button status for joystick 2
  7076.               add     sp,2          ; remove the argument
  7077.               or      _status2,ax   ; update status variable for joystick 2
  7078.  
  7079.               pop     es            ; restore altered registers
  7080.               pop     ds
  7081.               pop     si
  7082.               pop     di
  7083.               pop     dx
  7084.               pop     cx
  7085.               pop     bx
  7086.               pop     ax
  7087.               iret                  ; return from the interrupt routine
  7088.  
  7089.    handler    ENDP
  7090.  
  7091.    int1C_TEXT ENDS
  7092.  
  7093.               END
  7094.  
  7095.  
  7096.      The example just presented is not meant to be a tutorial on interrupts;
  7097. there are many good references on DOS that explain them in detail.  However,
  7098. an example specific to Fastgraph should be helpful.
  7099.  
  7100.  
  7101.  
  7102. Appendix D
  7103.  
  7104. Contents of the Compiler-Specific Libraries
  7105. 254   Fastgraph User's Guide
  7106.  
  7107.  
  7108.  
  7109.      For each of the supported Fastgraph compilers except QuickBASIC, there
  7110. is a compiler-specific Fastgraph library (also called the extended Fastgraph
  7111. library) that contains the following routines:
  7112.  
  7113.      fg_circlew        fg_getworld      fg_rectw          fg_setworld
  7114.      fg_clprectw       fg_initw         fg_restorew       fg_swchar
  7115.      fg_dashrw         fg_moverw        fg_savew          fg_swlength
  7116.      fg_dashw          fg_movew         fg_setangle       fg_swtext
  7117.      fg_drawrw         fg_paintw        fg_setclipw       fg_xscreen
  7118.      fg_draww          fg_panw          fg_setratio       fg_xworld
  7119.      fg_drectw         fg_pointw        fg_setsize        fg_yscreen
  7120.      fg_ellipsew       fg_polygonw      fg_setsizew       fg_yworld
  7121.  
  7122. All of these routines use the world space coordinate system, either directly
  7123. or internally.  Note that none of these routines are included in
  7124. Fastgraph/Light.
  7125.  
  7126.      As mentioned in chapter 1, if your program uses any of these routines,
  7127. you must link it with the standard Fastgraph library (FGx.LIB) and the
  7128. compiler-specific Fastgraph library (FGcccx.LIB).
  7129.