home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD2.mdf / c / library / dos / grafik / giflib11 / util / gif2bgi.c next >
Encoding:
C/C++ Source or Header  |  1990-11-02  |  27.5 KB  |  924 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 using the BGI device indepedent routines       *
  7. * Options:                                     *
  8. * -d BGI path : specify the directory where to look for bgi drivers.         *
  9. * -u BGIUserDriverName.Mode : use user driver instead of auto detection.     *
  10. * -z factor : zoom the pixels by the given factor.                 *
  11. * -b : beeps disabled.                                 *
  12. * -h : on line help.                                 *
  13. *                                         *
  14. *   In this file Screen refers to GIF file screen, while Device to BGI size. *
  15. ******************************************************************************
  16. * History:                                     *
  17. * 31 Jul 90 - Version 1.0 by Gershon Elber.                     *
  18. *****************************************************************************/
  19.  
  20. #include <graphics.h>
  21. #include <stdio.h>
  22. #include <stdlib.h>
  23. #include <conio.h>
  24. #include <ctype.h>
  25. #include <alloc.h>
  26. #include <string.h>
  27. #include <io.h>
  28. #include <dos.h>
  29. #include <bios.h>
  30. #include <fcntl.h>
  31. #include "gif_lib.h"
  32. #include "getarg.h"
  33.  
  34. #define PROGRAM_NAME    "Gif2BGI"
  35.  
  36. #define KEY_LEFT    256      /* Key Codes returned for operational keys */
  37. #define KEY_RIGHT    257      /* as return by the GetKey routine.         */
  38. #define KEY_UP        258
  39. #define KEY_DOWN    259
  40. #define KEY_RETURN    260
  41. #define KEY_DELETE    261
  42. #define KEY_INSERT    262
  43. #define KEY_BSPACE    263
  44. #define KEY_ESC        264
  45. #define KEY_HOME    265
  46. #define KEY_END        266
  47. #define KEY_PGUP    267
  48. #define KEY_PGDN    268
  49.  
  50. #define SET_POSITION_RESET    0        /* Situations need positionings: */
  51. #define SET_POSITION_ZOOM_U    1
  52. #define SET_POSITION_ZOOM_D    2
  53. #define SET_POSITION_PAN    3
  54.  
  55. #define CURSOR_TEXT_X    120
  56.  
  57. #define BGI_USER_INSTALL    999    /* BGI User installed driver device. */
  58.  
  59. extern unsigned int
  60.     _stklen = 16384;                 /* Increase default stack size. */
  61.  
  62. static char
  63.     *VersionStr =
  64.     PROGRAM_NAME
  65.     GIF_LIB_VERSION
  66.     "    Gershon Elber,    "
  67.     __DATE__ ",   " __TIME__ "\n"
  68.     "(C) Copyright 1989 Gershon Elber, Non commercial use only.\n";
  69. static char
  70.     *CtrlStr =
  71.     PROGRAM_NAME
  72.     " d%-BGI|Directory!s u%-UserBGIDrv.Mode!s z%-ZoomFactor!d b%- h%- GifFile!*s";
  73. static char
  74.     *GifFileName,
  75.     *BGIPath = "",
  76.     *BGIUserDriverName = NULL;
  77.  
  78. /* Make some variables global, so we could access them faster: */
  79. static int
  80.     ImageNum = 0,
  81.     BackGround = 0,
  82.     ForeGround = 1,               /* As close to white as possible. */
  83.     BeepsDisabled = FALSE,
  84.     ZoomFactor = 1,
  85.     BGIUserDriverMode = -1,
  86.     DeviceMaxX = 640, DeviceMaxY = 400,          /* Physical device dimensions. */
  87.     ScreenWidth = 320, ScreenHeight = 200,         /* Gif image screen size. */
  88.     InterlacedOffset[] = { 0, 4, 2, 1 }, /* The way Interlaced image should. */
  89.     InterlacedJumps[] = { 8, 8, 4, 2 };    /* be read - offsets and jumps... */
  90. static GifColorType
  91.     *ColorMap;
  92.  
  93. static int huge detectVGA(void);
  94. static void BGIInstallUserDriver(char *BGIUserDriverNameMode);
  95. static void DisplayScreen(GifRowType *ScreenBuffer, GifFileType *GifFile);
  96. static void PrintSettingStatus(GifFileType *GifFile);
  97. static void CPrintStr(char *Str, int y);
  98. static void SetPosition(int Why,
  99.                 int *ScreenLeft, int *ScreenTop,
  100.                 int *DeviceLeft, int *DeviceTop,
  101.                 int MoveX,       int MoveY);
  102. static void DrawScreen(GifRowType *ScreenBuffer,
  103.         int ScreenLeft, int ScreenTop, int DeviceLeft, int DeviceTop);
  104. static void DoCursorMode(GifRowType *ScreenBuffer,
  105.         int ScreenLeft, int ScreenTop, int DeviceLeft, int DeviceTop);
  106. static int MyKbHit(void);
  107. static int MyGetCh(void);
  108. static int GetKey(void);
  109. static void Tone(int Frequency, int Time);
  110.  
  111. /******************************************************************************
  112. * Interpret the command line and scan the given GIF file.              *
  113. ******************************************************************************/
  114. void main(int argc, char **argv)
  115. {
  116.     int    i, j, k, Error, NumFiles, Size, Row, Col, Width, Height, ExtCode,
  117.     Count, ColorMapSize, GraphDriver, GraphMode, Sum,
  118.     HelpFlag = FALSE,
  119.     BGIPathFlag = FALSE,
  120.     BGIUserDriverFlag = FALSE,
  121.     ZoomFlag = FALSE;
  122.     GifRecordType RecordType;
  123.     GifByteType *Extension;
  124.     char Str[80], *BGIUserDriverNameMode,
  125.     **FileName = NULL;
  126.     GifRowType *ScreenBuffer;
  127.     GifFileType *GifFile;
  128.     struct text_info TextInfo;      /* So we can restore starting text mode. */
  129.  
  130.     if ((Error = GAGetArgs(argc, argv, CtrlStr,
  131.         &BGIPathFlag, &BGIPath,
  132.         &BGIUserDriverFlag, &BGIUserDriverNameMode,
  133.         &ZoomFlag, &ZoomFactor,
  134.         &BeepsDisabled, &HelpFlag,
  135.         &NumFiles, &FileName)) != FALSE ||
  136.         (NumFiles > 1 && !HelpFlag)) {
  137.     if (Error)
  138.         GAPrintErrMsg(Error);
  139.     else if (NumFiles > 1)
  140.         GIF_MESSAGE("Error in command line parsing - one GIF file please.");
  141.     GAPrintHowTo(CtrlStr);
  142.     exit(1);
  143.     }
  144.  
  145.     if (HelpFlag) {
  146.     fprintf(stderr, VersionStr);
  147.     GAPrintHowTo(CtrlStr);
  148.     exit(0);
  149.     }
  150.  
  151.     if (BGIUserDriverFlag) {
  152.     /* Use the driver supplied by the user! */
  153.         BGIInstallUserDriver(BGIUserDriverNameMode);
  154.         installuserdriver(BGIUserDriverName, detectVGA);
  155.     GraphDriver = BGI_USER_INSTALL;
  156.     }
  157.     else {
  158.         /* Sense type of display we have and attempt to load right driver. */
  159.         detectgraph(&GraphDriver, &GraphMode);
  160.         if (GraphDriver < 0)
  161.         GIF_EXIT("BGI Auto detect: No graphics device detected.");
  162.     }
  163.  
  164.     /* Put in the following any graphic driver specific setup: */
  165.     switch (GraphDriver) {
  166.     case CGA:
  167.         GraphMode = CGAHI;
  168.         break;
  169.     case EGA:
  170.         GraphMode = EGAHI;
  171.         break;
  172.     case EGA64:
  173.         GraphMode = EGA64HI;
  174.         break;
  175.     case EGAMONO:
  176.         GraphMode = EGAMONOHI;
  177.         break;
  178.     case HERCMONO:
  179.         GraphMode = HERCMONOHI;
  180.         break;
  181.     case VGA:
  182.         GraphMode = VGAHI;
  183.         break;
  184.     case BGI_USER_INSTALL:
  185.         GraphDriver = DETECT;
  186.         GraphMode = BGIUserDriverMode;
  187.         break;
  188.     default:
  189.         GIF_EXIT("Requested graphic device is not supported.");
  190.         break;
  191.     }
  192.  
  193.     if (NumFiles == 1) {
  194.     GifFileName = *FileName;
  195.     if ((GifFile = DGifOpenFileName(*FileName)) == NULL) {
  196.         PrintGifError();
  197.         exit(-1);
  198.     }
  199.     }
  200.     else {
  201.     /* Use the stdin instead: */
  202.     GifFileName = "Stdin";
  203.     setmode(0, O_BINARY);
  204.     if ((GifFile = DGifOpenFileHandle(0)) == NULL) {
  205.         PrintGifError();
  206.         exit(-1);
  207.     }
  208.     }
  209.  
  210.     /* Allocate the screen as vector of column of rows. We cannt allocate    */
  211.     /* the all screen at once, as this broken minded CPU can allocate up to  */
  212.     /* 64k at a time and our image can be bigger than that:             */
  213.     /* Note this screen is device independent - its the screen as defined by */
  214.     /* the GIF file parameters itself.                         */
  215.     if ((ScreenBuffer = (GifRowType *)
  216.     malloc(GifFile -> SHeight * sizeof(GifRowType *))) == NULL)
  217.         GIF_EXIT("Failed to allocate memory required, aborted.");
  218.  
  219.     Size = GifFile -> SWidth * sizeof(GifPixelType);/* Size in bytes of one row.*/
  220.     if ((ScreenBuffer[0] = (GifRowType) malloc(Size)) == NULL)    /* First row. */
  221.     GIF_EXIT("Failed to allocate memory required, aborted.");
  222.  
  223.     for (i = 0; i < GifFile -> SWidth; i++)  /* Set its color to BackGround. */
  224.     ScreenBuffer[0][i] = GifFile -> SBackGroundColor;
  225.     for (i = 1; i < GifFile -> SHeight; i++) {
  226.     /* Allocate the other rows, andset their color to background too: */
  227.     if ((ScreenBuffer[i] = (GifRowType) malloc(Size)) == NULL)
  228.         GIF_EXIT("Failed to allocate memory required, aborted.");
  229.  
  230.     memcpy(ScreenBuffer[i], ScreenBuffer[0], Size);
  231.     }
  232.  
  233.     /* Scan the content of the GIF file and load the image(s) in: */
  234.     do {
  235.     if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR) {
  236.         PrintGifError();
  237.         exit(-1);
  238.     }
  239.     switch (RecordType) {
  240.         case IMAGE_DESC_RECORD_TYPE:
  241.         if (DGifGetImageDesc(GifFile) == GIF_ERROR) {
  242.             PrintGifError();
  243.             exit(-1);
  244.         }
  245.         Row = GifFile -> ITop; /* Image Position relative to Screen. */
  246.         Col = GifFile -> ILeft;
  247.         Width = GifFile -> IWidth;
  248.         Height = GifFile -> IHeight;
  249.         fprintf(stderr, "\n%s: Image %d at (%d, %d) [%dx%d]:     ",
  250.             PROGRAM_NAME, ++ImageNum, Col, Row, Width, Height);
  251.         if (GifFile -> ILeft + GifFile -> IWidth > GifFile -> SWidth ||
  252.            GifFile -> ITop + GifFile -> IHeight > GifFile -> SHeight) {
  253.             fprintf(stderr, "Image %d is not confined to screen dimension, aborted.\n");
  254.             exit(-2);
  255.         }
  256.         if (GifFile -> IInterlace) {
  257.             /* Need to perform 4 passes on the images: */
  258.             for (Count = i = 0; i < 4; i++)
  259.             for (j = Row + InterlacedOffset[i]; j < Row + Height;
  260.                          j += InterlacedJumps[i]) {
  261.                 fprintf(stderr, "\b\b\b\b%-4d", Count++);
  262.                 if (DGifGetLine(GifFile, &ScreenBuffer[j][Col],
  263.                 Width) == GIF_ERROR) {
  264.                 PrintGifError();
  265.                 exit(-1);
  266.                 }
  267.             }
  268.         }
  269.         else {
  270.             for (i = 0; i < Height; i++) {
  271.             fprintf(stderr, "\b\b\b\b%-4d", i);
  272.             if (DGifGetLine(GifFile, &ScreenBuffer[Row++][Col],
  273.                 Width) == GIF_ERROR) {
  274.                 PrintGifError();
  275.                 exit(-1);
  276.             }
  277.             }
  278.         }
  279.         break;
  280.         case EXTENSION_RECORD_TYPE:
  281.         /* Skip any extension blocks in file: */
  282.         if (DGifGetExtension(GifFile, &ExtCode, &Extension) == GIF_ERROR) {
  283.             PrintGifError();
  284.             exit(-1);
  285.         }
  286.         while (Extension != NULL) {
  287.             if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR) {
  288.             PrintGifError();
  289.             exit(-1);
  290.             }
  291.         }
  292.         break;
  293.         case TERMINATE_RECORD_TYPE:
  294.         break;
  295.         default:            /* Should be traps by DGifGetRecordType. */
  296.         break;
  297.     }
  298.     }
  299.     while (RecordType != TERMINATE_RECORD_TYPE);
  300.  
  301.     /* Lets display it - set the global variables required and do it: */
  302.     BackGround = GifFile -> SBackGroundColor;
  303.     ColorMap = (GifFile -> IColorMap ? GifFile -> IColorMap :
  304.                        GifFile -> SColorMap);
  305.     ColorMapSize = 1 << (GifFile -> IColorMap ? GifFile -> IBitsPerPixel :
  306.                         GifFile -> SBitsPerPixel);
  307.  
  308.     gettextinfo(&TextInfo);         /* Save current mode so we can recover. */
  309.  
  310.     initgraph(&GraphDriver, &GraphMode, BGIPath);
  311.     if (graphresult() != grOk)                /* Error occured during init. */
  312.     GIF_EXIT("Graphics System Error, failed to initialize driver.");
  313.  
  314.     if (getmaxcolor() + 1 < ColorMapSize) {
  315.     sprintf(Str, "GIF Image color map (%d) is too big for device (%d).\n",
  316.                           ColorMapSize, getmaxcolor() + 1);
  317.     closegraph();
  318.     GIF_EXIT(Str);
  319.     }
  320.  
  321.     /* Initialize hardware pallete and also select fore/background color.    */
  322.     BackGround = ForeGround = 1;
  323.     Sum = ((int) ColorMap[1].Red) +
  324.       ((int) ColorMap[1].Green) +
  325.       ((int) ColorMap[1].Blue);
  326.     j = k = Sum;
  327.     for (i = 0; i < ColorMapSize; i++) {
  328.     setrgbpalette(i, ColorMap[i].Red >> 2,
  329.              ColorMap[i].Green >> 2,
  330.              ColorMap[i].Blue >> 2);
  331.  
  332.     Sum = ((int) ColorMap[i].Red) +
  333.           ((int) ColorMap[i].Green) +
  334.           ((int) ColorMap[i].Blue);
  335.     if (i != 0 && Sum > j) {            /* Dont use color 0. */
  336.         ForeGround = i;
  337.         j = Sum;
  338.     }
  339.     if (i != 0 && Sum <= k) {            /* Dont use color 0. */
  340.         BackGround = i;
  341.         k = Sum;
  342.     }
  343.     }
  344.  
  345.     DeviceMaxX = getmaxx();            /* Read size of physical screen. */
  346.     DeviceMaxY = getmaxy();
  347.     ScreenWidth = GifFile -> SWidth;
  348.     ScreenHeight = GifFile -> SHeight;
  349.  
  350.     Tone(500, 10);
  351.     DisplayScreen(ScreenBuffer, GifFile);
  352.  
  353.     if (DGifCloseFile(GifFile) == GIF_ERROR) {
  354.     PrintGifError();
  355.     closegraph();
  356.     exit(-1);
  357.     }
  358.  
  359.     closegraph();
  360.  
  361.     textmode(TextInfo.currmode);
  362. }
  363.  
  364. /****************************************************************************
  365. * Routine to be called for the user installed driver:                *
  366. ****************************************************************************/
  367. static int huge detectVGA(void)
  368. {
  369.     return BGIUserDriverMode;
  370. }
  371.  
  372. /****************************************************************************
  373. * Routine to install the user BGI driver information.                *
  374. ****************************************************************************/
  375. static void BGIInstallUserDriver(char *BGIUserDriverNameMode)
  376. {
  377.     char *p;
  378.  
  379.     if ((p = strrchr(BGIUserDriverNameMode, '/')) != NULL ||
  380.     (p = strrchr(BGIUserDriverNameMode, '\\')) != NULL) {
  381.     p[0] = 0;
  382.     BGIPath = strdup(BGIUserDriverNameMode);
  383.     p++;
  384.     }
  385.  
  386.     p = strtok(p, ".");
  387.     BGIUserDriverName = strdup(p);
  388.  
  389.     p = strtok(NULL, ".");
  390.     if (sscanf(p, "%d", &BGIUserDriverMode) != 1 || BGIUserDriverName == NULL)
  391.     GIF_EXIT("User [-u] BGI specification has wrong format.");
  392. }
  393.  
  394. /******************************************************************************
  395. * Given the screen buffer, display it:                          *
  396. * The following commands are available (case insensitive).              *
  397. * 1. Four arrow to move along the screen (only if ScreenBuffer > physical     *
  398. *    screen in that direction.                              *
  399. * 2. C - goto cursor mode - print current color & position in GIF screen      *
  400. *        of the current pixel cursor is on.                      *
  401. * 3. D - zoom out by factor of 2.                          *
  402. * 4. R - redraw current image.                              *
  403. * 5. S - Print Current status/options.                          *
  404. * 6. U - zoom in by factor of 2.                          *
  405. * 7. ' ' - stop drawing current image.                          *
  406. * 8. ESC - to quit.                                  *
  407. ******************************************************************************/
  408. static void DisplayScreen(GifRowType *ScreenBuffer, GifFileType *GifFile)
  409. {
  410.     int DeviceTop, DeviceLeft,   /* Where ScreenBuffer is to mapped to ours. */
  411.     ScreenTop, ScreenLeft,  /* Porsion of ScreenBuffer to start display. */
  412.     XPanning, YPanning,         /* Amount to move using the arrows. */
  413.     GetK, DrawIt = TRUE;
  414.  
  415.     XPanning = DeviceMaxX / 2;
  416.     YPanning = DeviceMaxY / 2;
  417.  
  418.     SetPosition(SET_POSITION_RESET,
  419.         &ScreenLeft, &ScreenTop,
  420.         &DeviceLeft, &DeviceTop,
  421.         0, 0);
  422.  
  423.     do {
  424.     if (DrawIt && !MyKbHit()) {
  425.         DrawScreen(ScreenBuffer, ScreenLeft, ScreenTop,
  426.                      DeviceLeft, DeviceTop);
  427.         Tone(2000, 200);
  428.     }
  429.     DrawIt = TRUE;
  430.     switch (GetK = GetKey()) {
  431.         case 'C':
  432.         DoCursorMode(ScreenBuffer, ScreenLeft, ScreenTop,
  433.                        DeviceLeft, DeviceTop);
  434.         DrawIt = TRUE;
  435.         break;
  436.         case 'D':
  437.         if (ZoomFactor > 1) {
  438.             ZoomFactor >>= 1;
  439.             SetPosition(SET_POSITION_ZOOM_D,
  440.             &ScreenLeft, &ScreenTop,
  441.             &DeviceLeft, &DeviceTop,
  442.             0, 0);
  443.         }
  444.         else {
  445.             Tone(1000, 100);
  446.             DrawIt = FALSE;
  447.         }
  448.         break;
  449.         case 'R':
  450.         break;
  451.         case 'S':
  452.         PrintSettingStatus(GifFile);
  453.         break;
  454.         case 'U':
  455.         if (ZoomFactor < 256) {
  456.             ZoomFactor <<= 1;
  457.             SetPosition(SET_POSITION_ZOOM_U,
  458.             &ScreenLeft, &ScreenTop,
  459.             &DeviceLeft, &DeviceTop,
  460.             0, 0);
  461.         }
  462.         else {
  463.             Tone(1000, 100);
  464.             DrawIt = FALSE;
  465.         }
  466.         break;
  467.         case KEY_ESC:
  468.         break;
  469.         case KEY_LEFT:
  470.         SetPosition(SET_POSITION_PAN,
  471.             &ScreenLeft, &ScreenTop,
  472.             &DeviceLeft, &DeviceTop,
  473.             -XPanning, 0);
  474.         break;
  475.         case KEY_RIGHT:
  476.         SetPosition(SET_POSITION_PAN,
  477.             &ScreenLeft, &ScreenTop,
  478.             &DeviceLeft, &DeviceTop,
  479.             XPanning, 0);
  480.         break;
  481.         case KEY_UP:
  482.         SetPosition(SET_POSITION_PAN,
  483.             &ScreenLeft, &ScreenTop,
  484.             &DeviceLeft, &DeviceTop,
  485.             0, -YPanning);
  486.         break;
  487.         case KEY_DOWN:
  488.         SetPosition(SET_POSITION_PAN,
  489.             &ScreenLeft, &ScreenTop,
  490.             &DeviceLeft, &DeviceTop,
  491.             0, YPanning);
  492.         break;
  493.         default:
  494.         DrawIt = FALSE;
  495.         Tone(800, 100);
  496.         Tone(300, 200);
  497.         break;
  498.     }
  499.     }
  500.     while (GetK != KEY_ESC);
  501. }
  502.  
  503. /******************************************************************************
  504. * Routine to print (in text mode), current program status.              *
  505. ******************************************************************************/
  506. static void PrintSettingStatus(GifFileType *GifFile)
  507. {
  508.     char s[80];
  509.  
  510.     setcolor(ForeGround);
  511.  
  512.     CPrintStr(PROGRAM_NAME, 1);
  513.  
  514.     sprintf(s, "GIF File - %s.", GifFileName);
  515.     CPrintStr(s, 10);
  516.  
  517.     sprintf(s, "Gif Screen Size = [%d, %d]. Contains %d image(s).",
  518.     GifFile -> SWidth, GifFile -> SHeight, ImageNum);
  519.     CPrintStr(s, 20);
  520.  
  521.     if (GifFile -> SColorMap)
  522.     sprintf(s,
  523.         "Has Screen Color map of %d bits. BackGround = [%d, %d, %d].",
  524.         GifFile -> SBitsPerPixel,
  525.         GifFile -> SColorMap[GifFile -> SBackGroundColor].Red,
  526.         GifFile -> SColorMap[GifFile -> SBackGroundColor].Green,
  527.         GifFile -> SColorMap[GifFile -> SBackGroundColor].Blue);
  528.     else
  529.     sprintf(s, "No Screen color map.");
  530.     CPrintStr(s, 30);
  531.  
  532.     if (GifFile -> IColorMap)
  533.     sprintf(s, "Has Image map of %d bits (last image). Image is %s.",
  534.         GifFile -> IBitsPerPixel,
  535.         (GifFile -> IInterlace ? "interlaced" : "non interlaced"));
  536.     else
  537.     sprintf(s, "No Image color map.");
  538.     CPrintStr(s, 40);
  539.  
  540.     CPrintStr("Press anything to continue:", 60);
  541.     MyGetCh();
  542. }
  543.  
  544. /******************************************************************************
  545. * Routine to cprintf given string centered at given Y level, and attr:        *
  546. ******************************************************************************/
  547. static void CPrintStr(char *Str, int y)
  548. {
  549.     setfillstyle(SOLID_FILL, BackGround);
  550.     bar(0, y, textwidth(Str) + 2, y + textheight(Str) + 2);
  551.  
  552.     setcolor(ForeGround);
  553.     outtextxy(1, y + 1, Str);
  554. }
  555.  
  556. /******************************************************************************
  557. * Routine to set the position of Screen in Device, and what porsion of the    *
  558. * screen should be visible:                              *
  559. * MoveX, MoveY are the panning factors (if both zero - initialize).          *
  560. ******************************************************************************/
  561. static void SetPosition(int Why,
  562.                 int *ScreenLeft, int *ScreenTop,
  563.                 int *DeviceLeft, int *DeviceTop,
  564.                 int MoveX,       int MoveY)
  565. {
  566.  
  567.     MoveX /= ZoomFactor;     /* Make sure move move same amount independent. */
  568.     MoveY /= ZoomFactor;               /* of what ZommFactor is. */
  569.  
  570.     /* Figure out position of GIF file in real device X axis: */
  571.     if (ScreenWidth * ZoomFactor <= DeviceMaxX + 1) {
  572.     /* Device is big enough to hold all the image X axis: */
  573.     *ScreenLeft = 0;
  574.     *DeviceLeft = (DeviceMaxX - ScreenWidth * ZoomFactor) / 2;
  575.     }
  576.     else {
  577.     /* Device is too small to hold all the image X axis: */
  578.     switch (Why) {
  579.         case SET_POSITION_RESET:
  580.         *ScreenLeft = 0;
  581.         break;
  582.         case SET_POSITION_ZOOM_U:
  583.         *ScreenLeft += DeviceMaxX / (2 * ZoomFactor);
  584.         break;
  585.         case SET_POSITION_ZOOM_D:
  586.         *ScreenLeft -= DeviceMaxX / (4 * ZoomFactor);
  587.         break;
  588.         case SET_POSITION_PAN:
  589.         if (MoveX != 0) *ScreenLeft += MoveX;
  590.         break;
  591.     }
  592.     if (*ScreenLeft < 0) *ScreenLeft = 0;
  593.     if ((ScreenWidth - *ScreenLeft) * ZoomFactor < DeviceMaxX + 1)
  594.         *ScreenLeft = (ScreenWidth * ZoomFactor -
  595.                         DeviceMaxX + 1) / ZoomFactor;
  596.     *DeviceLeft = 0;
  597.     }
  598.  
  599.     /* Figure out position of GIF file in real device Y axis: */
  600.     if (ScreenHeight * ZoomFactor <= DeviceMaxY + 1) {
  601.     /* Device is big enough to hold all the image Y axis: */
  602.     *ScreenTop = 0;
  603.     *DeviceTop = (DeviceMaxY - ScreenHeight * ZoomFactor) / 2;
  604.     }
  605.     else {
  606.     /* Device is too small to hold all the image Y axis: */
  607.     switch (Why) {
  608.         case SET_POSITION_RESET:
  609.         *ScreenTop = 0;
  610.         break;
  611.         case SET_POSITION_ZOOM_U:
  612.         *ScreenTop += DeviceMaxY / (2 * ZoomFactor);
  613.         break;
  614.         case SET_POSITION_ZOOM_D:
  615.         *ScreenTop -= DeviceMaxY / (4 * ZoomFactor);
  616.         break;
  617.         case SET_POSITION_PAN:
  618.         if (MoveY != 0) *ScreenTop += MoveY;
  619.         break;
  620.     }
  621.     if (*ScreenTop < 0) *ScreenTop = 0;
  622.     if ((ScreenHeight - *ScreenTop) * ZoomFactor < DeviceMaxY + 1)
  623.         *ScreenTop = (ScreenHeight * ZoomFactor -
  624.                          DeviceMaxY - 1) / ZoomFactor;
  625.     *DeviceTop = 0;
  626.     }
  627.  
  628.     /* Make sure the position is on Byte boundary (8 pixels per byte): */
  629.     *DeviceLeft &= 0xfff8;
  630. }
  631.  
  632. /******************************************************************************
  633. * The real drawing of the image is performed here. Few things are taken into  *
  634. * account:                                      *
  635. * 1. The zoom factor. If > 1 each pixel is multiplied this amount vertically  *
  636. *    and horizontally.                                  *
  637. *   The image is drawn from ScreenBuffer ScreenTop/Left in the bottom/right   *
  638. * directions, onto the Device DeviceTop/Left in the bottom/right direction    *
  639. *   Pressing space during drawing will abort this routine.              *
  640. ******************************************************************************/
  641. static void DrawScreen(GifRowType *ScreenBuffer,
  642.         int ScreenLeft, int ScreenTop, int DeviceLeft, int DeviceTop)
  643. {
  644.     int i, j, k, l, CountZoomJ, CountZoomI,
  645.     DeviceWidth, DeviceRight, ScreenBottom;
  646.     unsigned char *ImageBuffer, *p;
  647.     GifPixelType *Line;
  648.  
  649.     /* Make sure we start from scratch. Note cleardevice() uses color 0 even */
  650.     /* if it may be non black.                             */
  651.     cleardevice();
  652.  
  653.     if (getmaxcolor() + 1 == 256) {
  654.     /* Optimize this case - one byte per pixel. */
  655.         DeviceWidth = ScreenWidth * ZoomFactor;
  656.     if (DeviceWidth + DeviceLeft > DeviceMaxX)
  657.         DeviceWidth = DeviceMaxX - DeviceLeft;
  658.         DeviceRight = DeviceLeft + DeviceWidth - 1;
  659.         ScreenBottom = ScreenTop + ScreenHeight - 1;
  660.  
  661.     if ((ImageBuffer = malloc(imagesize(DeviceLeft, DeviceTop,
  662.                         DeviceRight, DeviceTop))) == NULL)
  663.         GIF_EXIT("Failed to allocate memory required, aborted.");
  664.     getimage(DeviceLeft, DeviceTop, DeviceRight, DeviceTop, ImageBuffer);
  665.  
  666.     for (k = DeviceTop; k < DeviceMaxY && ScreenTop <= ScreenBottom;) {
  667.         Line = ScreenBuffer[ScreenTop++];
  668.         p = ImageBuffer + 4;      /* point on first pixel in bitmap. */
  669.         if (ZoomFactor == 1)
  670.         memcpy(p, &Line[ScreenLeft], DeviceWidth);
  671.         else {
  672.         for (i = 0, j = ScreenLeft, CountZoomI = ZoomFactor;
  673.              i < DeviceWidth;
  674.              i++) {
  675.             *p++ = Line[j];
  676.             if (--CountZoomI == 0) {
  677.             CountZoomI = ZoomFactor;
  678.             j++;
  679.             }
  680.         }
  681.         }
  682.  
  683.         /* Abort drawing if space bar was pressed: */
  684.         if (MyKbHit() && GetKey() == ' ') break;
  685.  
  686.         for (i = 0; i < ZoomFactor; i++)
  687.         putimage(DeviceLeft, k++, ImageBuffer, COPY_PUT);
  688.     }
  689.  
  690.     free((char *) ImageBuffer);
  691.     }
  692.     else {
  693.         for (CountZoomJ = ZoomFactor, j = ScreenTop, l = DeviceTop;
  694.          j < ScreenHeight && l <= DeviceMaxY; l++) {
  695.         Line = ScreenBuffer[j];
  696.  
  697.         /* Abort drawing if space bar was pressed: */
  698.         if (MyKbHit() && GetKey() == ' ') break;
  699.  
  700.         for (CountZoomI = ZoomFactor, i = ScreenLeft, k = DeviceLeft;
  701.              i < ScreenWidth && k <= DeviceMaxX;) {
  702.             putpixel(k++, l, Line[i]);
  703.  
  704.             if (!--CountZoomI) {
  705.             /* Go to next column: */
  706.             i++;
  707.             CountZoomI = ZoomFactor;
  708.             }
  709.         }
  710.  
  711.         if (!--CountZoomJ) {
  712.             /* Go to next row: */
  713.             j++;
  714.             CountZoomJ = ZoomFactor;
  715.         }
  716.         }
  717.     }
  718. }
  719.  
  720. /******************************************************************************
  721. * Walks along the current image, while printing pixel value and position.     *
  722. * 4 arrows may be used, and any other key will abort this operation          *
  723. ******************************************************************************/
  724. static void DoCursorMode(GifRowType *ScreenBuffer,
  725.         int ScreenLeft, int ScreenTop, int DeviceLeft, int DeviceTop)
  726. {
  727.     int GetK, DeviceRight, DeviceBottom, x, y, Step;
  728.     GifPixelType Pixel;
  729.     char s[80];
  730.  
  731.     DeviceRight = DeviceLeft + (ScreenWidth - ScreenLeft) * ZoomFactor;
  732.     if (DeviceRight > DeviceMaxX) DeviceRight = DeviceMaxX;
  733.  
  734.     DeviceBottom = DeviceTop + (ScreenHeight - ScreenTop) * ZoomFactor;
  735.     if (DeviceBottom > DeviceMaxY) DeviceBottom = DeviceMaxY;
  736.  
  737.     x = (DeviceLeft + DeviceRight) / 2;
  738.     y = (DeviceTop + DeviceBottom) / 2;
  739.  
  740.     setwritemode(XOR_PUT);
  741.  
  742.     while (TRUE) {
  743.     Pixel = ScreenBuffer[ScreenTop + (y - DeviceTop) / ZoomFactor]
  744.                 [ScreenLeft + (x - DeviceLeft) / ZoomFactor];
  745.     sprintf(s, "Color = %3d [%3d, %3d, %3d], X = %3d, Y = %3d",
  746.         Pixel,
  747.         ColorMap[Pixel].Red,
  748.         ColorMap[Pixel].Green,
  749.         ColorMap[Pixel].Blue,
  750.         (x - DeviceLeft) / ZoomFactor,
  751.         (y - DeviceTop) / ZoomFactor);
  752.  
  753.     setfillstyle(SOLID_FILL, BackGround);
  754.     bar(0, 0, textwidth(s) + 2, textheight(s) + 2);
  755.  
  756.         setcolor(ForeGround);
  757.     outtextxy(1, 1, s);
  758.  
  759.     line(0, y, DeviceMaxX, y);
  760.     line(x, 0, x, DeviceMaxY);
  761.     GetK = GetKey();
  762.     line(0, y, DeviceMaxX, y);
  763.     line(x, 0, x, DeviceMaxY);
  764.  
  765.     Step = 10;
  766.     switch (GetK) {
  767.         case '1':
  768.         GetK = KEY_END;
  769.         break;
  770.         case '2':
  771.         GetK = KEY_DOWN;
  772.         break;
  773.         case '3':
  774.         GetK = KEY_PGDN;
  775.         break;
  776.         case '4':
  777.         GetK = KEY_LEFT;
  778.         break;
  779.         case '6':
  780.         GetK = KEY_RIGHT;
  781.         break;
  782.         case '7':
  783.         GetK = KEY_HOME;
  784.         break;
  785.         case '8':
  786.         GetK = KEY_UP;
  787.         break;
  788.         case '9':
  789.         GetK = KEY_PGUP;
  790.         break;
  791.         default:
  792.         Step = 1;
  793.     }
  794.  
  795.     switch (GetK) {
  796.         case KEY_LEFT:
  797.         x -= Step;
  798.         break;
  799.         case KEY_RIGHT:
  800.         x += Step;
  801.         break;
  802.         case KEY_UP:
  803.         y -= Step;
  804.         break;
  805.         case KEY_DOWN:
  806.         y += Step;
  807.         break;
  808.         case KEY_PGUP:
  809.         y -= Step;
  810.         x += Step;
  811.         break;
  812.         case KEY_PGDN:
  813.         y += Step;
  814.         x += Step;
  815.         break;
  816.         case KEY_HOME:
  817.         y -= Step;
  818.         x -= Step;
  819.         break;
  820.         case KEY_END:
  821.         y += Step;
  822.         x -= Step;
  823.         break;
  824.         default:
  825.         setwritemode(COPY_PUT);
  826.         return;
  827.     }
  828.     if (x < DeviceLeft) x = DeviceLeft;
  829.     if (x >= DeviceRight) x = DeviceRight;
  830.     if (y < DeviceTop) y = DeviceTop;
  831.     if (y >= DeviceBottom) y = DeviceBottom;
  832.     }
  833. }
  834.  
  835. /******************************************************************************
  836. * Return non zero value if at list one character exists in keyboard queue.    *
  837. * This routine emulates kbhit() which do uses stdin and useless for us.       *
  838. ******************************************************************************/
  839. static int MyKbHit(void)
  840. {
  841.     return bioskey(1);
  842. }
  843.  
  844. /******************************************************************************
  845. * Get a key from keyboard directly (bypass stdin as we might redirect it).    *
  846. * This routine emulates getch() which do uses stdin and useless for us.       *
  847. ******************************************************************************/
  848. static int MyGetCh(void)
  849. {
  850.     static int Extended = 0;
  851.     int c;
  852.  
  853.     if (Extended) {
  854.     c = Extended;
  855.     Extended = 0;
  856.     return c;
  857.     }
  858.     else {
  859.     c = bioskey(0);
  860.     if (c & 0x0ff)
  861.         return c;
  862.     else {
  863.         Extended = c >> 8;
  864.         return 0;
  865.     }
  866.     }
  867. }
  868.  
  869. /******************************************************************************
  870. * Get a key from keyboard, and translating operational keys into special      *
  871. * codes (>255).    Lower case characters are upercased.                  *
  872. ******************************************************************************/
  873. static int GetKey(void)
  874. {
  875.     char c;
  876.  
  877.     while (TRUE) switch (c = MyGetCh()) {
  878.     case 0:              /* Extended code - get the next extended char. */
  879.         switch (MyGetCh()) {
  880.         case 75: return KEY_LEFT;
  881.         case 77: return KEY_RIGHT;
  882.         case 72: return KEY_UP;
  883.         case 80: return KEY_DOWN;
  884.         case 71: return KEY_HOME;
  885.         case 79: return KEY_END;
  886.         case 73: return KEY_PGUP;
  887.         case 81: return KEY_PGDN;
  888.         case 83: return KEY_DELETE;
  889.         case 82: return KEY_INSERT;
  890.         }
  891.         break;
  892.     case 8:
  893.         return KEY_BSPACE;
  894.     case 10:
  895.     case 13:
  896.         return KEY_RETURN;
  897.     case 27:
  898.         return KEY_ESC;
  899.     default:
  900.         if (isprint(c)) {
  901.         if (islower(c))
  902.             return toupper(c);
  903.         else
  904.             return c;
  905.         }
  906.         else {
  907.         Tone(800, 100);
  908.         Tone(300, 200);
  909.         }
  910.     }
  911. }
  912.  
  913. /******************************************************************************
  914. * Routine to make some sound with given Frequency, Time milliseconds:          *
  915. ******************************************************************************/
  916. static void Tone(int Frequency, int Time)
  917. {
  918.     if (BeepsDisabled) return;
  919.  
  920.     sound(Frequency);
  921.     delay(Time);
  922.     nosound();
  923. }
  924.