home *** CD-ROM | disk | FTP | other *** search
/ Media Share 9 / MEDIASHARE_09.ISO / graphics / gif-util.zip / GIF2HERC.C < prev    next >
C/C++ Source or Header  |  1989-08-01  |  36KB  |  1,112 lines

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