home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD2.mdf / c / library / dos / grafik / giflib11 / util / gif2herc.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-09-03  |  34.9 KB  |  1,108 lines

  1. /*****************************************************************************
  2. *   "Gif-Lib" - Yet another gif library.                     *
  3. *                                         *
  4. * Written by:  Gershon Elber                Ver 0.1, Jul. 1989   *
  5. ******************************************************************************
  6. * Program to display GIF file on hercules device                 *
  7. * Options:                                     *
  8. * -d factor : use dithering of matrix of size factor by factor.             *
  9. * -z factor : zoom the pixels by the given factor.                 *
  10. * -t level : set the threshold level of white in the result (0..100).         *
  11. * -m mapping : methods for mapping the 24bits colors into 1 BW bit.         *
  12. * -i : invert the image.                             *
  13. * -b : beeps disabled.                                 *
  14. * -h : on line help.                                 *
  15. *                                         *
  16. *   This program uses TC2.0 hercules graphic driver.                 *
  17. *   In this file Screen refers to GIF file screen, while Device to Hercules. *
  18. ******************************************************************************
  19. * History:                                     *
  20. * 1 Jul 89 - Version 1.0 by Gershon Elber.                     *
  21. *****************************************************************************/
  22.  
  23. #include <graphics.h>
  24. #include <stdio.h>
  25. #include <stdlib.h>
  26. #include <conio.h>
  27. #include <ctype.h>
  28. #include <alloc.h>
  29. #include <string.h>
  30. #include <io.h>
  31. #include <dos.h>
  32. #include <bios.h>
  33. #include <fcntl.h>
  34. #include "gif_lib.h"
  35. #include "getarg.h"
  36.  
  37. #define PROGRAM_NAME    "Gif2Herc"
  38.  
  39. #define KEY_LEFT    256      /* Key Codes returned for operational keys */
  40. #define KEY_RIGHT    257      /* as return by the GetKey routine.         */
  41. #define KEY_UP        258
  42. #define KEY_DOWN    259
  43. #define KEY_RETURN    260
  44. #define KEY_DELETE    261
  45. #define KEY_INSERT    262
  46. #define KEY_BSPACE    263
  47. #define KEY_ESC        264
  48. #define KEY_HOME    265
  49. #define KEY_END        266
  50. #define KEY_PGUP    267
  51. #define KEY_PGDN    268
  52.  
  53. #define    C2BW_BACK_GROUND    0 /*Methods to map 24bits Colors to 1 BW bit.*/
  54. #define    C2BW_GREY_LEVELS    1
  55. #define C2BW_DITHER        2
  56. #define C2BW_NUM_METHODS    3            /* Always hold # of methods. */
  57.  
  58. #define DEFAULT_THRESHOLD    5000         /* Color -> BW threshold level. */
  59. #define INCREMENT_THRESHOLD    1000
  60.  
  61. #define DITHER_MIN_MATRIX    2
  62. #define DITHER_MAX_MATRIX    4
  63.  
  64. #define NORMAL_ATTR    0x07                 /* Text attributes. */
  65. #define INVERSE_ATTR    0x70
  66. #define BLINK_ATTR    0x90
  67.  
  68. #define SET_POSITION_RESET    0        /* Situations need positionings: */
  69. #define SET_POSITION_ZOOM_U    1
  70. #define SET_POSITION_ZOOM_D    2
  71. #define SET_POSITION_PAN    3
  72.  
  73. #define DEVICE_BASE    0xb000              /* Hercules frame buffer base. */
  74. #define DEVICE_PAGE0    0xb000
  75. #define DEVICE_PAGE1    0xb800
  76. #define HERC_MAX_X    719
  77. #define HERC_MAX_Y    347
  78.  
  79. #define CURSOR_TEXT_X    120
  80.  
  81. extern unsigned int
  82.     _stklen = 16384;                 /* Increase default stack size. */
  83.  
  84. static char
  85.     *VersionStr =
  86.     PROGRAM_NAME
  87.     GIF_LIB_VERSION
  88.     "    Gershon Elber,    "
  89.     __DATE__ ",   " __TIME__ "\n"
  90.     "(C) Copyright 1989 Gershon Elber, Non commercial use only.\n";
  91. static char
  92.     *CtrlStr =
  93.     PROGRAM_NAME
  94.     " d%-DitherSize!d z%-ZoomFactor!d t%-BWThreshold!d m%-Mapping!d i%- b%- h%- GifFile!*s";
  95. static char
  96.     *GifFileName;
  97. /* Make some variables global, so we could access them faster: */
  98. static int
  99.     ImageNum = 0,
  100.     BackGround = 0,
  101.     BeepsDisabled = FALSE,
  102.     DitherSize = 2, DitherFlag = FALSE,
  103.     ZoomFactor = 1, ZoomFlag = FALSE,
  104.     BWThresholdFlag = FALSE, Threshold,
  105.     BWThreshold = DEFAULT_THRESHOLD,       /* Color -> BW mapping threshold. */
  106.     Mapping, MappingFlag = FALSE,
  107.     InvertFlag = FALSE,
  108.     HelpFlag = FALSE,
  109.     ColorToBWMapping = C2BW_BACK_GROUND,
  110.     InterlacedOffset[] = { 0, 4, 2, 1 }, /* The way Interlaced image should  */
  111.     InterlacedJumps[] = { 8, 8, 4, 2 };    /* be read - offsets and jumps... */
  112. static GifColorType
  113.     *ColorMap;
  114.  
  115.  
  116. static void DisplayScreen(GifRowType *ScreenBuffer, GifFileType *GifFile);
  117. static void PrintSettingStatus(GifFileType *GifFile, GifRowType *DitherBuffer);
  118. static void CPrintStr(char *Str, int y, int attr);
  119. static void SetPositon(int Why,
  120.                int ScreenWidth, int ScreenHeight,
  121.                int DeviceMaxX,  int DeviceMaxY,
  122.                int *ScreenLeft, int *ScreenTop,
  123.                int *DeviceLeft, int *DeviceTop,
  124.                int MoveX,       int MoveY);
  125. static void ClearGraphDevice(void);
  126. static void OpenGraphDevice(void);
  127. static void CloseGraphDevice(void);
  128. static void EvalDitheredScanline(GifRowType *ScreenBuffer, int Row,
  129.                     int RowSize, GifRowType *DitherBuffer);
  130. static void DrawScreen(GifRowType *ScreenBuffer, GifRowType *DitherBuffer,
  131.     int DeviceTop, int DeviceLeft, int DeviceMaxX, int DeviceMaxY,
  132.     int ScreenTop, int ScreenLeft, int ScreenWidth, int ScreenHeight);
  133. static void DoCursorMode(GifRowType *ScreenBuffer,
  134.     int ScreenLeft, int ScreenTop, int ScreenWidth, int ScreenHeight,
  135.     int DeviceLeft, int DeviceTop);
  136. static int MyKbHit(void);
  137. static int MyGetCh(void);
  138. static int GetKey(void);
  139. static void Tone(int Frequency, int Time);
  140.  
  141. /******************************************************************************
  142. * Interpret the command line and scan the given GIF file.              *
  143. ******************************************************************************/
  144. void main(int argc, char **argv)
  145. {
  146.     int    i, j, Error, NumFiles, Size, Row, Col, Width, Height, ExtCode, Count;
  147.     GifRecordType RecordType;
  148.     GifByteType *Extension;
  149.     char **FileName = NULL;
  150.     GifRowType *ScreenBuffer;
  151.     GifFileType *GifFile;
  152.  
  153.     if ((Error = GAGetArgs(argc, argv, CtrlStr, &DitherFlag, &DitherSize,
  154.         &ZoomFlag, &ZoomFactor, &BWThresholdFlag, &Threshold,
  155.         &MappingFlag, &Mapping,    &InvertFlag, &BeepsDisabled, &HelpFlag,
  156.         &NumFiles, &FileName)) != FALSE ||
  157.         (NumFiles > 1 && !HelpFlag)) {
  158.     if (Error)
  159.         GAPrintErrMsg(Error);
  160.     else if (NumFiles > 1)
  161.         GIF_MESSAGE("Error in command line parsing - one GIF file please.");
  162.     GAPrintHowTo(CtrlStr);
  163.     exit(1);
  164.     }
  165.  
  166.     if (HelpFlag) {
  167.     fprintf(stderr, VersionStr);
  168.     GAPrintHowTo(CtrlStr);
  169.     exit(0);
  170.     }
  171.  
  172.     if (DitherFlag) {
  173.     /* Make sure we are o.k.: */
  174.     if (DitherSize > DITHER_MAX_MATRIX) DitherSize = DITHER_MAX_MATRIX;
  175.     if (DitherSize < DITHER_MIN_MATRIX) DitherSize = DITHER_MAX_MATRIX;
  176.     }
  177.  
  178.     /* As Threshold is in [0..100] range and BWThreshold is [0..25500]: */
  179.     if (BWThresholdFlag) {
  180.     if (Threshold > 100 || Threshold < 0)
  181.         GIF_EXIT("Threshold not in 0..100 percent.");
  182.     BWThreshold = Threshold * 255;
  183.     if (BWThreshold == 0) BWThreshold = 1;   /* Overcome divide by zero! */
  184.     }
  185.  
  186.     /* No message is emitted, but mapping method is clipped to exists method.*/
  187.     if (MappingFlag) ColorToBWMapping = Mapping % C2BW_NUM_METHODS;
  188.  
  189.     if (NumFiles == 1) {
  190.     GifFileName = *FileName;
  191.     if ((GifFile = DGifOpenFileName(*FileName)) == NULL) {
  192.         PrintGifError();
  193.         exit(-1);
  194.     }
  195.     }
  196.     else {
  197.     /* Use the stdin instead: */
  198.     GifFileName = "Stdin";
  199.     setmode(0, O_BINARY);
  200.     if ((GifFile = DGifOpenFileHandle(0)) == NULL) {
  201.         PrintGifError();
  202.         exit(-1);
  203.     }
  204.     }
  205.  
  206.     /* Allocate the screen as vector of column of rows. We cannt allocate    */
  207.     /* the all screen at once, as this broken minded CPU can allocate up to  */
  208.     /* 64k at a time and our image can be bigger than that:             */
  209.     /* Note this screen is device independent - its the screen as defined by */
  210.     /* the GIF file parameters itself.                         */
  211.     if ((ScreenBuffer = (GifRowType *)
  212.     malloc(GifFile -> SHeight * sizeof(GifRowType *))) == NULL)
  213.         GIF_EXIT("Failed to allocate memory required, aborted.");
  214.  
  215.     Size = GifFile -> SWidth * sizeof(GifPixelType);/* Size in bytes one row.*/
  216.     if ((ScreenBuffer[0] = (GifRowType) malloc(Size)) == NULL) /* First row. */
  217.     GIF_EXIT("Failed to allocate memory required, aborted.");
  218.  
  219.     for (i = 0; i < GifFile -> SWidth; i++)  /* Set its color to BackGround. */
  220.     ScreenBuffer[0][i] = GifFile -> SBackGroundColor;
  221.     for (i = 1; i < GifFile -> SHeight; i++) {
  222.     /* Allocate the other rows, andset their color to background too: */
  223.     if ((ScreenBuffer[i] = (GifRowType) malloc(Size)) == NULL)
  224.         GIF_EXIT("Failed to allocate memory required, aborted.");
  225.  
  226.     memcpy(ScreenBuffer[i], ScreenBuffer[0], Size);
  227.     }
  228.  
  229.     /* Scan the content of the GIF file and load the image(s) in: */
  230.     do {
  231.     if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR) {
  232.         PrintGifError();
  233.         exit(-1);
  234.     }
  235.     switch (RecordType) {
  236.         case IMAGE_DESC_RECORD_TYPE:
  237.         if (DGifGetImageDesc(GifFile) == GIF_ERROR) {
  238.             PrintGifError();
  239.             exit(-1);
  240.         }
  241.         Row = GifFile -> ITop; /* Image Position relative to Screen. */
  242.         Col = GifFile -> ILeft;
  243.         Width = GifFile -> IWidth;
  244.         Height = GifFile -> IHeight;
  245.         fprintf(stderr, "\n%s: Image %d at (%d, %d) [%dx%d]:     ",
  246.             PROGRAM_NAME, ++ImageNum, Col, Row, Width, Height);
  247.         if (GifFile -> ILeft + GifFile -> IWidth > GifFile -> SWidth ||
  248.            GifFile -> ITop + GifFile -> IHeight > GifFile -> SHeight) {
  249.             fprintf(stderr, "Image %d is not confined to screen dimension, aborted\n");
  250.             exit(-2);
  251.         }
  252.         if (GifFile -> IInterlace) {
  253.             /* Need to perform 4 passes on the images: */
  254.             for (Count = i = 0; i < 4; i++)
  255.             for (j = Row + InterlacedOffset[i]; j < Row + Height;
  256.                          j += InterlacedJumps[i]) {
  257.                 fprintf(stderr, "\b\b\b\b%-4d", Count++);
  258.                 if (DGifGetLine(GifFile, &ScreenBuffer[j][Col],
  259.                 Width) == GIF_ERROR) {
  260.                 PrintGifError();
  261.                 exit(-1);
  262.                 }
  263.             }
  264.         }
  265.         else {
  266.             for (i = 0; i < Height; i++) {
  267.             fprintf(stderr, "\b\b\b\b%-4d", i);
  268.             if (DGifGetLine(GifFile, &ScreenBuffer[Row++][Col],
  269.                 Width) == GIF_ERROR) {
  270.                 PrintGifError();
  271.                 exit(-1);
  272.             }
  273.             }
  274.         }
  275.         break;
  276.         case EXTENSION_RECORD_TYPE:
  277.         /* Skip any extension blocks in file: */
  278.         if (DGifGetExtension(GifFile, &ExtCode, &Extension) == GIF_ERROR) {
  279.             PrintGifError();
  280.             exit(-1);
  281.         }
  282.         while (Extension != NULL) {
  283.             if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR) {
  284.             PrintGifError();
  285.             exit(-1);
  286.             }
  287.         }
  288.         break;
  289.         case TERMINATE_RECORD_TYPE:
  290.         break;
  291.         default:            /* Should be traps by DGifGetRecordType. */
  292.         break;
  293.     }
  294.     }
  295.     while (RecordType != TERMINATE_RECORD_TYPE);
  296.  
  297.     /* Lets display it - set the global variables required and do it: */
  298.     BackGround = GifFile -> SBackGroundColor;
  299.     ColorMap = (GifFile -> IColorMap ? GifFile -> IColorMap :
  300.                        GifFile -> SColorMap);
  301.     Tone(500, 10);
  302.     DisplayScreen(ScreenBuffer, GifFile);
  303.  
  304.     if (DGifCloseFile(GifFile) == GIF_ERROR) {
  305.     PrintGifError();
  306.     exit(-1);
  307.     }
  308. }
  309.  
  310. /******************************************************************************
  311. * Given the screen buffer, display it:                          *
  312. * The following commands are available (case insensitive).              *
  313. * 1. Four arrow to move along the screen (only if ScreenBuffer > physical     *
  314. *    screen in that direction.                              *
  315. * 2. C - goto cursor mode - print current color & position in GIF screen      *
  316. *        of the current pixel cursor is on.                      *
  317. * 3. D - zoom out by factor of 2.                          *
  318. * 4. H - halftoning dithering matrix resize.                      *
  319. * 5. I - invert the image.                              *
  320. * 6. M - toggles method of Color -> BW mapping.                      *
  321. * 7. R - redraw current image.                              *
  322. * 8. S - Print Current status/options.                          *
  323. * 9. U - zoom in by factor of 2.                          *
  324. * 10. ' ' - stop drawing current image.                          *
  325. * 11. ESC - to quit.                                  *
  326. ******************************************************************************/
  327. static void DisplayScreen(GifRowType *ScreenBuffer, GifFileType *GifFile)
  328. {
  329.     int i, j, Size,
  330.     DeviceTop, DeviceLeft,   /* Where ScreenBuffer is to mapped to ours. */
  331.     ScreenTop, ScreenLeft,  /* Porsion of ScreenBuffer to start display. */
  332.     DeviceMaxX, DeviceMaxY,              /* Physical device dimensions. */
  333.     XPanning, YPanning,         /* Amount to move using the arrows. */
  334.     GetK, DrawIt = TRUE;
  335.     GifRowType *DitherBuffer;         /* Used to save dithered pixel scanned. */
  336.  
  337.     OpenGraphDevice();
  338.     DeviceMaxX = HERC_MAX_X;            /* Read size of physical screen. */
  339.     DeviceMaxY = HERC_MAX_Y;
  340.  
  341.     XPanning = DeviceMaxX / 2;
  342.     YPanning = DeviceMaxY / 2;
  343.  
  344.     SetPositon(SET_POSITION_RESET, GifFile -> SWidth, GifFile -> SHeight,
  345.         DeviceMaxX, DeviceMaxY,
  346.         &ScreenLeft, &ScreenTop,
  347.         &DeviceLeft, &DeviceTop,
  348.         0, 0);
  349.  
  350.     /*   Allocate the buffer to save the dithered information. If fails to   */
  351.     /* allocate, set it to NULL, and no dithering will take place.         */
  352.     if ((DitherBuffer = (GifRowType *)
  353.     malloc(DITHER_MAX_MATRIX * sizeof(GifRowType *))) != NULL) {
  354.     Size = GifFile -> SWidth * sizeof(GifPixelType); /* Size of one row. */
  355.     for (i = 0; i < DITHER_MAX_MATRIX; i++) {
  356.         if ((DitherBuffer[i] = (GifRowType) malloc(Size)) == NULL) {
  357.         for (j = 0; j < i; j++) free((char *) DitherBuffer[i]);
  358.         free((char *) DitherBuffer);
  359.         DitherBuffer = NULL;
  360.         break;
  361.         }
  362.     }
  363.     }
  364.     if (DitherBuffer == NULL) {
  365.     Tone(300, 100);
  366.     Tone(100, 300);
  367.     }
  368.  
  369.     do {
  370.     if (DrawIt && !MyKbHit()) {
  371.         DrawScreen(ScreenBuffer, DitherBuffer,
  372.         DeviceTop, DeviceLeft, DeviceMaxX, DeviceMaxY,
  373.         ScreenTop, ScreenLeft, GifFile -> SWidth, GifFile -> SHeight);
  374.         Tone(2000, 200);
  375.     }
  376.     DrawIt = TRUE;
  377.     switch (GetK = GetKey()) {
  378.         case 'C':
  379.         DoCursorMode(ScreenBuffer, ScreenLeft, ScreenTop,
  380.                  GifFile -> SWidth, GifFile -> SHeight,
  381.                  DeviceLeft, DeviceTop);
  382.         DrawIt = FALSE;
  383.         break;
  384.         case 'D':
  385.         if (ZoomFactor > 1) {
  386.             ZoomFactor >>= 1;
  387.             SetPositon(SET_POSITION_ZOOM_D,
  388.             GifFile -> SWidth, GifFile -> SHeight,
  389.             DeviceMaxX, DeviceMaxY,
  390.             &ScreenLeft, &ScreenTop,
  391.             &DeviceLeft, &DeviceTop,
  392.             0, 0);
  393.         }
  394.         else {
  395.             Tone(1000, 100);
  396.             DrawIt = FALSE;
  397.         }
  398.         break;
  399.         case 'H':
  400.         if (++DitherSize > DITHER_MAX_MATRIX)
  401.             DitherSize = DITHER_MIN_MATRIX;
  402.         break;
  403.         case 'I':
  404.         InvertFlag = !InvertFlag;
  405.         break;
  406.         case 'M':
  407.         ColorToBWMapping = (ColorToBWMapping + 1) % C2BW_NUM_METHODS;
  408.  
  409.         break;
  410.         case 'R':
  411.         break;
  412.         case 'S':
  413.         PrintSettingStatus(GifFile, DitherBuffer);
  414.         break;
  415.         case 'U':
  416.         if (ZoomFactor < 256) {
  417.             ZoomFactor <<= 1;
  418.             SetPositon(SET_POSITION_ZOOM_U,
  419.             GifFile -> SWidth, GifFile -> SHeight,
  420.             DeviceMaxX, DeviceMaxY,
  421.             &ScreenLeft, &ScreenTop,
  422.             &DeviceLeft, &DeviceTop,
  423.             0, 0);
  424.         }
  425.         else {
  426.             Tone(1000, 100);
  427.             DrawIt = FALSE;
  428.         }
  429.         break;
  430.         case KEY_ESC:
  431.         break;
  432.         case KEY_LEFT:
  433.         SetPositon(SET_POSITION_PAN,
  434.             GifFile -> SWidth, GifFile -> SHeight,
  435.             DeviceMaxX, DeviceMaxY,
  436.             &ScreenLeft, &ScreenTop,
  437.             &DeviceLeft, &DeviceTop,
  438.             -XPanning, 0);
  439.         break;
  440.         case KEY_RIGHT:
  441.         SetPositon(SET_POSITION_PAN,
  442.             GifFile -> SWidth, GifFile -> SHeight,
  443.             DeviceMaxX, DeviceMaxY,
  444.             &ScreenLeft, &ScreenTop,
  445.             &DeviceLeft, &DeviceTop,
  446.             XPanning, 0);
  447.         break;
  448.         case KEY_UP:
  449.         SetPositon(SET_POSITION_PAN,
  450.             GifFile -> SWidth, GifFile -> SHeight,
  451.             DeviceMaxX, DeviceMaxY,
  452.             &ScreenLeft, &ScreenTop,
  453.             &DeviceLeft, &DeviceTop,
  454.             0, -YPanning);
  455.         break;
  456.         case KEY_DOWN:
  457.         SetPositon(SET_POSITION_PAN,
  458.             GifFile -> SWidth, GifFile -> SHeight,
  459.             DeviceMaxX, DeviceMaxY,
  460.             &ScreenLeft, &ScreenTop,
  461.             &DeviceLeft, &DeviceTop,
  462.             0, YPanning);
  463.         break;
  464.         case KEY_DELETE:
  465.         BWThreshold += INCREMENT_THRESHOLD;
  466.         if (BWThreshold == 0) BWThreshold = 1;
  467.         break;
  468.         case KEY_INSERT:
  469.         BWThreshold -= INCREMENT_THRESHOLD;
  470.         if (BWThreshold == 0) BWThreshold = 1;
  471.         break;
  472.         default:
  473.         DrawIt = FALSE;
  474.         Tone(800, 100);
  475.         Tone(300, 200);
  476.         break;
  477.     }
  478.     }
  479.     while (GetK != KEY_ESC);
  480.  
  481.     CloseGraphDevice();
  482. }
  483.  
  484. /******************************************************************************
  485. * Routine to print (in text mode), current program status.              *
  486. ******************************************************************************/
  487. static void PrintSettingStatus(GifFileType *GifFile, GifRowType *DitherBuffer)
  488. {
  489.     char s[80];
  490.  
  491.     CloseGraphDevice();
  492.  
  493.     CPrintStr(PROGRAM_NAME, 1, INVERSE_ATTR);
  494.  
  495.     sprintf(s, "GIF File - %s", GifFileName);
  496.     CPrintStr(s, 3, NORMAL_ATTR);
  497.  
  498.     sprintf(s, "Gif Screen Size = [%d, %d]. Contains %d image(s).",
  499.     GifFile -> SWidth, GifFile -> SHeight, ImageNum);
  500.     CPrintStr(s, 5, NORMAL_ATTR);
  501.  
  502.     if (GifFile -> SColorMap)
  503.     sprintf(s,
  504.         "Has Screen Color map of %d bits. BackGround = [%d, %d, %d]",
  505.         GifFile -> SBitsPerPixel,
  506.         GifFile -> SColorMap[GifFile -> SBackGroundColor].Red,
  507.         GifFile -> SColorMap[GifFile -> SBackGroundColor].Green,
  508.         GifFile -> SColorMap[GifFile -> SBackGroundColor].Blue);
  509.     else
  510.     sprintf(s, "No Screen color map.");
  511.     CPrintStr(s, 7, NORMAL_ATTR);
  512.  
  513.     if (GifFile -> IColorMap)
  514.     sprintf(s, "Has Image map of %d bits (last image). Image is %s.",
  515.         GifFile -> IBitsPerPixel,
  516.         (GifFile -> IInterlace ? "interlaced" : "non interlaced"));
  517.     else
  518.     sprintf(s, "No Image color map.");
  519.     CPrintStr(s, 9, NORMAL_ATTR);
  520.  
  521.     sprintf(s, "Color to BW threshold level - %d%%.\n", BWThreshold / 255);
  522.     CPrintStr(s, 11, NORMAL_ATTR);
  523.  
  524.     CPrintStr("Color To BW mapping:", 15, NORMAL_ATTR);
  525.     switch(ColorToBWMapping) {
  526.     case C2BW_BACK_GROUND:
  527.         CPrintStr("Color != BackGround", 16, NORMAL_ATTR);
  528.         break;
  529.     case C2BW_GREY_LEVELS:
  530.         CPrintStr(".3 * R + .59 * G + .11 * B > threshold", 16,
  531.                                 NORMAL_ATTR);
  532.         break;
  533.     case C2BW_DITHER:
  534.         sprintf(s, ".3 * R + .59 * G + .11 * B dithered (Size = %d).",
  535.         DitherSize);
  536.         CPrintStr(s, 16, NORMAL_ATTR);
  537.         break;
  538.     }
  539.  
  540.     sprintf(s, "Dither Buffer %s (Size = %d), Zoom = %d.",
  541.     DitherBuffer ? "allocated succesfully" : "not allocated (failed)",
  542.     DitherSize, ZoomFactor);
  543.  
  544.     CPrintStr(s, 18, NORMAL_ATTR);
  545.  
  546.     CPrintStr("Press anything to continue:", 23, BLINK_ATTR);
  547.     MyGetCh();
  548.  
  549.     OpenGraphDevice();
  550. }
  551.  
  552. /******************************************************************************
  553. * Routine to cprintf given string centered at given Y level, and attr:        *
  554. ******************************************************************************/
  555. static void CPrintStr(char *Str, int y, int attr)
  556. {
  557.     gotoxy(40 - (strlen(Str) + 1) / 2, y);
  558.     textattr(attr);
  559.     cputs(Str);
  560. }
  561.  
  562. /******************************************************************************
  563. * Routine to set the position of Screen in Device, and what porsion of the    *
  564. * screen should be visible:                              *
  565. * MoveX, MoveY are the panning factors (if both zero - initialize).          *
  566. ******************************************************************************/
  567. static void SetPositon(int Why,
  568.                int ScreenWidth, int ScreenHeight,
  569.                int DeviceMaxX,  int DeviceMaxY,
  570.                int *ScreenLeft, int *ScreenTop,
  571.                int *DeviceLeft, int *DeviceTop,
  572.                int MoveX,       int MoveY)
  573. {
  574.  
  575.     MoveX /= ZoomFactor;       /* Make sure move same amount independent */
  576.     MoveY /= ZoomFactor;               /* of what ZoomFactor is. */
  577.  
  578.     /* Figure out position of GIF file in real device X axis: */
  579.     if (ScreenWidth * ZoomFactor <= DeviceMaxX + 1) {
  580.     /* Device is big enough to hold all the image X axis: */
  581.     *ScreenLeft = 0;
  582.     *DeviceLeft = (DeviceMaxX - ScreenWidth * ZoomFactor) / 2;
  583.     }
  584.     else {
  585.     /* Device is too small to hold all the image X axis: */
  586.     switch (Why) {
  587.         case SET_POSITION_RESET:
  588.         *ScreenLeft = 0;
  589.         break;
  590.         case SET_POSITION_ZOOM_U:
  591.         *ScreenLeft += DeviceMaxX / (2 * ZoomFactor);
  592.         break;
  593.         case SET_POSITION_ZOOM_D:
  594.         *ScreenLeft -= DeviceMaxX / (4 * ZoomFactor);
  595.         break;
  596.         case SET_POSITION_PAN:
  597.         if (MoveX != 0) *ScreenLeft += MoveX;
  598.         break;
  599.     }
  600.     if (*ScreenLeft < 0) *ScreenLeft = 0;
  601.     if ((ScreenWidth - *ScreenLeft) * ZoomFactor < DeviceMaxX + 1)
  602.         *ScreenLeft = (ScreenWidth * ZoomFactor -
  603.                         DeviceMaxX + 1) / ZoomFactor;
  604.     *DeviceLeft = 0;
  605.     }
  606.  
  607.     /* Figure out position of GIF file in real device Y axis: */
  608.     if (ScreenHeight * ZoomFactor <= DeviceMaxY + 1) {
  609.     /* Device is big enough to hold all the image Y axis: */
  610.     *ScreenTop = 0;
  611.     *DeviceTop = (DeviceMaxY - ScreenHeight * ZoomFactor) / 2;
  612.     }
  613.     else {
  614.     /* Device is too small to hold all the image Y axis: */
  615.     switch (Why) {
  616.         case SET_POSITION_RESET:
  617.         *ScreenTop = 0;
  618.         break;
  619.         case SET_POSITION_ZOOM_U:
  620.         *ScreenTop += DeviceMaxY / (2 * ZoomFactor);
  621.         break;
  622.         case SET_POSITION_ZOOM_D:
  623.         *ScreenTop -= DeviceMaxY / (4 * ZoomFactor);
  624.         break;
  625.         case SET_POSITION_PAN:
  626.         if (MoveY != 0) *ScreenTop += MoveY;
  627.         break;
  628.     }
  629.     if (*ScreenTop < 0) *ScreenTop = 0;
  630.     if ((ScreenHeight - *ScreenTop) * ZoomFactor < DeviceMaxY + 1)
  631.         *ScreenTop = (ScreenHeight * ZoomFactor -
  632.                          DeviceMaxY - 1) / ZoomFactor;
  633.     *DeviceTop = 0;
  634.     }
  635.  
  636.     /* Make sure the position is on Byte boundary (8 pixels per byte): */
  637.     *DeviceLeft &= 0xfff8;
  638. }
  639.  
  640. /******************************************************************************
  641. * Routine to clear graphic device:                          *
  642. ******************************************************************************/
  643. static void ClearGraphDevice(void)
  644. {
  645.     cleardevice();
  646. }
  647.  
  648. /******************************************************************************
  649. * Routine to open graphic device:                          *
  650. ******************************************************************************/
  651. static void OpenGraphDevice(void)
  652. {
  653.     int GraphDriver = HERCMONO, GraphMode = HERCMONOHI;
  654.  
  655.     if (registerbgidriver(Herc_driver) < 0)
  656.     GIF_EXIT("Cannt register graphic device.");
  657.  
  658.     initgraph(&GraphDriver, &GraphMode, "");
  659.     if (graphresult() != grOk)
  660.     GIF_EXIT("Graphics System Error (No Hercules!?).");
  661. }
  662.  
  663. /*****************************************************************************
  664. * Routine to close and shutdown    graphic    mode :                     *
  665. *****************************************************************************/
  666. static void CloseGraphDevice(void)
  667. {
  668.     closegraph();              /* Return the system to text mode. */
  669. }
  670.  
  671. /*****************************************************************************
  672. * Routine to evaluate dithered scanlines out of given ones, using Size         *
  673. * dithering matrix, starting from Row. The given scanlines are NOT modified. *
  674. *****************************************************************************/
  675. static void EvalDitheredScanline(GifRowType *ScreenBuffer, int Row,
  676.                      int RowSize, GifRowType *DitherBuffer)
  677. {
  678.     static char Dither2[2][2] = {     /* See Foley & Van Dam pp. 597-601. */
  679.     { 1, 3 },
  680.     { 4, 2 }
  681.     };
  682.     static char Dither3[3][3] = {
  683.     { 7, 9, 5 },
  684.     { 2, 1, 4 },
  685.     { 6, 3, 8 }
  686.     };
  687.     static char Dither4[4][4] = {
  688.     { 1,  9,  3,  11 },
  689.     { 13, 5,  15, 7 },
  690.     { 4,  12, 2,  10 },
  691.     { 16, 8,  14, 6 }
  692.     };
  693.     int i, j, k, Level;
  694.     long Intensity;
  695.     GifColorType *ColorMapEntry;
  696.  
  697.     /* Scan the Rows (Size rows) evaluate intensity every Size pixel and use */
  698.     /* the dither matrix to set the dithered result;                 */
  699.     for (i = 0; i <= RowSize - DitherSize; i += DitherSize) {
  700.     Intensity = 0;
  701.     for (j = Row; j < Row + DitherSize; j++)
  702.         for (k = 0; k < DitherSize; k++) {
  703.         ColorMapEntry = &ColorMap[ScreenBuffer[j][i+k]];
  704.         Intensity += 30 * ((int) ColorMapEntry->Red) +
  705.                  59 * ((int) ColorMapEntry->Green) +
  706.                  11 * ((int) ColorMapEntry->Blue);
  707.         }
  708.  
  709.     /* Find the intensity level (between 0 and Size^2) of our matrix: */
  710.     /* Expression is "Intensity * BWThreshold / (25500 * DefThresh)"  */
  711.     /* but to prevent from overflow in the long evaluation we do this:*/
  712.     Level = ((Intensity / 2550) * ((long) DEFAULT_THRESHOLD) /
  713.                         (((long) BWThreshold) * 10));
  714.     switch (DitherSize) {
  715.         case 2:
  716.         for (j = 0; j < DitherSize; j++)
  717.             for (k = 0; k < DitherSize; k++)
  718.             DitherBuffer[j][i+k] = Dither2[j][k] <= Level;
  719.         break;
  720.         case 3:
  721.         for (j = 0; j < DitherSize; j++)
  722.             for (k = 0; k < DitherSize; k++)
  723.             DitherBuffer[j][i+k] = Dither3[j][k] <= Level;
  724.         break;
  725.         case 4:
  726.         for (j = 0; j < DitherSize; j++)
  727.             for (k = 0; k < DitherSize; k++)
  728.             DitherBuffer[j][i+k] = Dither4[j][k] <= Level;
  729.         break;
  730.     }
  731.     }
  732. }
  733.  
  734. /******************************************************************************
  735. * The real drawing of the image is performed here. Few things are taken into  *
  736. * account:                                      *
  737. * 1. The zoom factor. If > 1 each pixel is multiplied this amount vertically  *
  738. *    and horizontally.                                  *
  739. * 2. The Invert flag. If TRUE each pixel before drawn is inverted.          *
  740. * 3. The rendering mode and dither matrix flag if dithering is selected.      *
  741. *   The image is drawn from ScreenBuffer ScreenTop/Left in the bottom/right   *
  742. * directions, onto the Device DeviceTop/Left in the bottom/right direction    *
  743. *     This routine was optimized for the hercules graphic card and should be  *
  744. * handled carfully as it is device dependent.                      *
  745. *   Pressing space during drawing will abort this routine.              *
  746. ******************************************************************************/
  747. static void DrawScreen(GifRowType *ScreenBuffer, GifRowType *DitherBuffer,
  748.     int DeviceTop, int DeviceLeft, int DeviceMaxX, int DeviceMaxY,
  749.     int ScreenTop, int ScreenLeft, int ScreenWidth, int ScreenHeight)
  750. {
  751.     unsigned int Offset;
  752.     int i, j, k, l, m, CountZoomJ, CountZoomI,
  753.     DitheredLinesLeft = 0, DitheredLinesCount, MapInvert[2];
  754.     GifByteType DeviceByte;
  755.     GifPixelType *Line;
  756.     GifColorType *ColorMapEntry;
  757.  
  758.     ClearGraphDevice();             /* Make sure we start from scratch. */
  759.  
  760.     if (InvertFlag) {           /* Make the inversion as fast a possible. */
  761.     MapInvert[0] = 1;
  762.     MapInvert[1] = 0;
  763.     }
  764.     else {
  765.     MapInvert[0] = 0;
  766.     MapInvert[1] = 1;
  767.     }
  768.  
  769.     for (CountZoomJ = ZoomFactor, j = ScreenTop, l = DeviceTop, DeviceByte = 0;
  770.      j < ScreenHeight && l <= DeviceMaxY; l++) {
  771.     Line = ScreenBuffer[j];
  772.  
  773.     /* We are going to access the hercules frame buffer directly: */
  774.     Offset = 0x2000 * (l & 0x03) + (l >> 2) * 90 + (DeviceLeft >> 3);
  775.  
  776.     /* Abort drawing if space bar was pressed: */
  777.     if (MyKbHit() && GetKey() == ' ') return;
  778.  
  779.     /* We decide right here what method to map Colors to BW so the inner */
  780.     /* loop will be independent of it (and therefore faster):         */
  781.     switch(ColorToBWMapping) {
  782.         case C2BW_BACK_GROUND:
  783.         for (CountZoomI = ZoomFactor, i = ScreenLeft, k = DeviceLeft, m = 0;
  784.              i < ScreenWidth && k <= DeviceMaxX;) {
  785.             /* The following lines are equivalent to the putpixel    */
  786.             /* (and making it real machine dependent...) by almost   */
  787.             /* factor of 3:                         */
  788.             /* putpixel(k++, l, MapInvert[ColorToBW(Line[i])]);         */
  789.             DeviceByte = (DeviceByte << 1) +
  790.                     MapInvert[Line[i] != BackGround];
  791.             if (++m == 8) {
  792.             /* We have byte - place it on hercules frame buffer: */
  793.             k += 8;
  794.             pokeb(DEVICE_BASE, Offset++, DeviceByte);
  795.             m = 0;
  796.             }
  797.  
  798.             if (!--CountZoomI) {
  799.             /* Go to next column: */
  800.             i++;
  801.             CountZoomI = ZoomFactor;
  802.             }
  803.         }
  804.         if (k < DeviceMaxX) {
  805.             /* Poke last byte also: */
  806.             DeviceByte <<= 8 - m;
  807.             pokeb(DEVICE_BASE, Offset++, DeviceByte);
  808.         }
  809.         break;
  810.         case C2BW_GREY_LEVELS:
  811.         for (CountZoomI = ZoomFactor, i = ScreenLeft, k = DeviceLeft, m = 0;
  812.              i < ScreenWidth && k <= DeviceMaxX;) {
  813.             /* The following lines are equivalent to the putpixel    */
  814.             /* (and making it real machine dependent...) by almost   */
  815.             /* factor of 3:                         */
  816.             /* putpixel(k++, l, MapInvert[ColorToBW(Line[i])]);         */
  817.  
  818.             ColorMapEntry = &ColorMap[Line[i]];
  819.             /* For the transformation from RGB to BW, see Folley &   */
  820.             /* Van Dam pp 613: The Y channel is the BW we need:         */
  821.             /* As colors are 255 maximum, the result can be up to    */
  822.             /* 25500 which is still in range of our 16 bits integers.*/
  823.             DeviceByte = (DeviceByte << 1) +
  824.             MapInvert[(30 * (int) ColorMapEntry->Red) +
  825.                    59 * ((int) ColorMapEntry->Green) +
  826.                    11 * ((int) ColorMapEntry->Blue) >
  827.                     BWThreshold];
  828.             if (++m == 8) {
  829.             /* We have byte - place it on hercules frame buffer: */
  830.             k += 8;
  831.             pokeb(DEVICE_BASE, Offset++, DeviceByte);
  832.             m = 0;
  833.             }
  834.  
  835.             if (!--CountZoomI) {
  836.             /* Go to next column: */
  837.             i++;
  838.             CountZoomI = ZoomFactor;
  839.             }
  840.         }
  841.         if (k < DeviceMaxX) {
  842.             /* Poke last byte also: */
  843.             DeviceByte <<= 8 - m;
  844.             pokeb(DEVICE_BASE, Offset++, DeviceByte);
  845.         }
  846.         break;
  847.         case C2BW_DITHER:
  848.         if (DitheredLinesLeft-- == 0) {
  849.             EvalDitheredScanline(ScreenBuffer,
  850.             (j < ScreenHeight - DitherSize ? j :
  851.                          ScreenHeight - DitherSize),
  852.             ScreenWidth, DitherBuffer);
  853.             DitheredLinesLeft = DitherSize - 1;
  854.             DitheredLinesCount = 0;
  855.         }
  856.         Line = DitherBuffer[DitheredLinesCount++];
  857.         for (CountZoomI = ZoomFactor, i = ScreenLeft, k = DeviceLeft, m = 0;
  858.              i < ScreenWidth && k <= DeviceMaxX;) {
  859.             /* The following lines are equivalent to the putpixel    */
  860.             /* (and making it real machine dependent...) by almost   */
  861.             /* factor of 3:                         */
  862.             /* putpixel(k++, l, MapInvert[Line[i]]);             */
  863.             DeviceByte = (DeviceByte << 1) + MapInvert[Line[i]];
  864.             if (++m == 8) {
  865.             /* We have byte - place it on hercules frame buffer: */
  866.             k += 8;
  867.             pokeb(DEVICE_BASE, Offset++, DeviceByte);
  868.             m = 0;
  869.             }
  870.  
  871.             if (!--CountZoomI) {
  872.             /* Go to next column: */
  873.             i++;
  874.             CountZoomI = ZoomFactor;
  875.             }
  876.         }
  877.         if (k < DeviceMaxX) {
  878.             /* Poke last byte also: */
  879.             DeviceByte <<= 8 - m;
  880.             pokeb(DEVICE_BASE, Offset++, DeviceByte);
  881.         }
  882.         break;
  883.     }
  884.  
  885.     if (!--CountZoomJ) {
  886.         /* Go to next row: */
  887.         j++;
  888.         CountZoomJ = ZoomFactor;
  889.     }
  890.     }
  891. }
  892.  
  893. /******************************************************************************
  894. * Walks along the current image, while printing pixel value and position.     *
  895. * 4 arrows may be used, and any other key will abort this operation          *
  896. * As there is no XOR mode for text, we copy all Page 0 to Page 1 before we    *
  897. * start this, and copy it back each time...                      *
  898. ******************************************************************************/
  899. static void DoCursorMode(GifRowType *ScreenBuffer,
  900.     int ScreenLeft, int ScreenTop, int ScreenWidth, int ScreenHeight,
  901.     int DeviceLeft, int DeviceTop)
  902. {
  903.     int GetK, DeviceRight, DeviceBottom, x, y, CursorTextY, Step;
  904.     GifPixelType Pixel;
  905.     char s[80];
  906.     char far *Page0, *Page1;
  907.  
  908.     Page0 = MK_FP(DEVICE_PAGE0, 0);
  909.     Page1 = MK_FP(DEVICE_PAGE1, 0);
  910.  
  911.     memcpy(Page1, Page0, 0x8000);
  912.  
  913.  
  914.     DeviceRight = DeviceLeft + (ScreenWidth - ScreenLeft) * ZoomFactor;
  915.     if (DeviceRight > HERC_MAX_X) DeviceRight = HERC_MAX_X;
  916.  
  917.     DeviceBottom = DeviceTop + (ScreenHeight - ScreenTop) * ZoomFactor;
  918.     if (DeviceBottom > HERC_MAX_Y) DeviceBottom = HERC_MAX_Y;
  919.  
  920.     x = (DeviceLeft + DeviceRight) / 2;
  921.     y = (DeviceTop + DeviceBottom) / 2;
  922.  
  923.     while (TRUE) {
  924.     Pixel = ScreenBuffer[ScreenTop + (y - DeviceTop) / ZoomFactor]
  925.                 [ScreenLeft + (x - DeviceLeft) / ZoomFactor];
  926.     sprintf(s, "Color = %d [%d, %d, %d], X = %d, Y = %d.",
  927.         Pixel,
  928.         ColorMap[Pixel].Red,
  929.         ColorMap[Pixel].Green,
  930.         ColorMap[Pixel].Blue,
  931.         (x - DeviceLeft) / ZoomFactor,
  932.         (y - DeviceTop) / ZoomFactor);
  933.     CursorTextY = (y > HERC_MAX_Y / 2 ? HERC_MAX_Y / 4 :
  934.                         3 * HERC_MAX_Y / 4);
  935.     setviewport(CURSOR_TEXT_X, CursorTextY,
  936.             CURSOR_TEXT_X + textwidth(s), CursorTextY + textheight(s),
  937.             TRUE);
  938.     clearviewport();
  939.     setviewport(0, 0, HERC_MAX_X, HERC_MAX_Y, TRUE);
  940.     setcolor(1);                 /* We only have one color here. */
  941.     line(0, y, HERC_MAX_X, y);
  942.     line(x, 0, x, HERC_MAX_Y);
  943.     outtextxy(CURSOR_TEXT_X, CursorTextY, s);
  944.     GetK = GetKey();
  945.  
  946.     memcpy(Page0, Page1, 0x8000);
  947.  
  948.     Step = 10;
  949.     switch (GetK) {
  950.         case '1':
  951.         GetK = KEY_END;
  952.         break;
  953.         case '2':
  954.         GetK = KEY_DOWN;
  955.         break;
  956.         case '3':
  957.         GetK = KEY_PGDN;
  958.         break;
  959.         case '4':
  960.         GetK = KEY_LEFT;
  961.         break;
  962.         case '6':
  963.         GetK = KEY_RIGHT;
  964.         break;
  965.         case '7':
  966.         GetK = KEY_HOME;
  967.         break;
  968.         case '8':
  969.         GetK = KEY_UP;
  970.         break;
  971.         case '9':
  972.         GetK = KEY_PGUP;
  973.         break;
  974.         default:
  975.         Step = 1;
  976.     }
  977.  
  978.     switch (GetK) {
  979.         case KEY_LEFT:
  980.         x -= Step;
  981.         break;
  982.         case KEY_RIGHT:
  983.         x += Step;
  984.         break;
  985.         case KEY_UP:
  986.         y -= Step;
  987.         break;
  988.         case KEY_DOWN:
  989.         y += Step;
  990.         break;
  991.         case KEY_PGUP:
  992.         y -= Step;
  993.         x += Step;
  994.         break;
  995.         case KEY_PGDN:
  996.         y += Step;
  997.         x += Step;
  998.         break;
  999.         case KEY_HOME:
  1000.         y -= Step;
  1001.         x -= Step;
  1002.         break;
  1003.         case KEY_END:
  1004.         y += Step;
  1005.         x -= Step;
  1006.         break;
  1007.         default:
  1008.         return;
  1009.     }
  1010.     if (x < DeviceLeft) x = DeviceLeft;
  1011.     if (x >= DeviceRight) x = DeviceRight;
  1012.     if (y < DeviceTop) y = DeviceTop;
  1013.     if (y >= DeviceBottom) y = DeviceBottom;
  1014.     }
  1015. }
  1016.  
  1017. /******************************************************************************
  1018. * Return non zero value if at list one character exists in keyboard queue.    *
  1019. * This routine emulates kbhit() which do uses stdin and useless for us.       *
  1020. ******************************************************************************/
  1021. static int MyKbHit(void)
  1022. {
  1023.     return bioskey(1);
  1024. }
  1025.  
  1026. /******************************************************************************
  1027. * Get a key from keyboard directly (bypass stdin as we might redirect it).    *
  1028. * This routine emulates getch() which do uses stdin and useless for us.       *
  1029. ******************************************************************************/
  1030. static int MyGetCh(void)
  1031. {
  1032.     static int Extended = 0;
  1033.     int c;
  1034.  
  1035.     if (Extended) {
  1036.     c = Extended;
  1037.     Extended = 0;
  1038.     return c;
  1039.     }
  1040.     else {
  1041.     c = bioskey(0);
  1042.     if (c & 0x0ff)
  1043.         return c;
  1044.     else {
  1045.         Extended = c >> 8;
  1046.         return 0;
  1047.     }
  1048.     }
  1049. }
  1050.  
  1051. /******************************************************************************
  1052. * Get a key from keyboard, and translating operational keys into special      *
  1053. * codes (>255).    Lower case characters are upercased.                  *
  1054. ******************************************************************************/
  1055. static int GetKey(void)
  1056. {
  1057.     char c;
  1058.  
  1059.     while (TRUE) switch (c = MyGetCh()) {
  1060.     case 0:              /* Extended code - get the next extended char. */
  1061.         switch (MyGetCh()) {
  1062.         case 75: return KEY_LEFT;
  1063.         case 77: return KEY_RIGHT;
  1064.         case 72: return KEY_UP;
  1065.         case 80: return KEY_DOWN;
  1066.         case 71: return KEY_HOME;
  1067.         case 79: return KEY_END;
  1068.         case 73: return KEY_PGUP;
  1069.         case 81: return KEY_PGDN;
  1070.         case 83: return KEY_DELETE;
  1071.         case 82: return KEY_INSERT;
  1072.         }
  1073.         break;
  1074.     case 8:
  1075.         return KEY_BSPACE;
  1076.     case 10:
  1077.     case 13:
  1078.         return KEY_RETURN;
  1079.     case 27:
  1080.         return KEY_ESC;
  1081.     default:
  1082.         if (isprint(c)) {
  1083.         if (islower(c))
  1084.             return toupper(c);
  1085.         else
  1086.             return c;
  1087.         }
  1088.         else {
  1089.         Tone(800, 100);
  1090.         Tone(300, 200);
  1091.         }
  1092.     }
  1093.  
  1094.     return 0;                   /* Should never be here any case. */
  1095. }
  1096.  
  1097. /******************************************************************************
  1098. * Routine to make some sound with given Frequency, Time milliseconds:          *
  1099. ******************************************************************************/
  1100. static void Tone(int Frequency, int Time)
  1101. {
  1102.     if (BeepsDisabled) return;
  1103.  
  1104.     sound(Frequency);
  1105.     delay(Time);
  1106.     nosound();
  1107. }
  1108.