home *** CD-ROM | disk | FTP | other *** search
/ Piper's Pit BBS/FTP: ibm 0040 - 0049 / ibm0040-0049 / ibm0040.tar / ibm0040 / IMGPROC.ZIP / C5CVIDEO.ZIP / CVIDEO.C next >
Encoding:
C/C++ Source or Header  |  1990-04-06  |  39.4 KB  |  1,312 lines

  1. /*  
  2. Copyright 1990 by John Wiley & Sons, Inc.
  3.           All Rights Reserved.
  4. */
  5. /**********************************************************/
  6. /***                                                    ***/
  7. /***              Video Digitizer Program               ***/
  8. /***    produces color images from monochrome camera    ***/
  9. /***      utilizes the 256 color 320x200 VGA mode       ***/
  10. /***                written in Turbo C                  ***/
  11. /***                        by                          ***/
  12. /***                 Craig A. Lindley                   ***/
  13. /***       Median Cut Algorithm code developed by       ***/
  14. /***                 Dan Butterfield                    ***/
  15. /***                                                    ***/
  16. /***  Usage:                                            ***/
  17. /***  cvideo [-a -d -h -o -p -s -v -x] filename <cr>    ***/
  18. /***      -a correct aspect ratio of image              ***/
  19. /***      -d use Floyd-Steinberg dithering              ***/
  20. /***      -h or ? show help                             ***/
  21. /***      -o calculates optimum color palette           ***/
  22. /***      -p create PCX output file                     ***/
  23. /***      -s scales color register values to max        ***/
  24. /***      -v displays program progress information      ***/
  25. /***      -x create executable display program          ***/
  26. /***       filename is name given to output files       ***/
  27. /***                                                    ***/
  28. /***         Ver: 1.0    Last Update: 12/05/89          ***/
  29. /**********************************************************/
  30.  
  31. #include <stdio.h>
  32. #include <conio.h>
  33. #include <dos.h>
  34. #include <process.h>
  35. #include <graphics.h>
  36. #include <string.h>
  37. #include <alloc.h>
  38. #include <math.h>
  39. #include "vga.h"
  40. #include "misc.h"
  41. #include "pcx.h"
  42. #include "digitize.h"
  43.  
  44. /*
  45. Please see chapter 12 for a complete discussion of aspect ratio
  46. problems and correction techniques. What we will do in this
  47. program is to acquire three 320x240 images and compress them into
  48. three 320x200 images correcting the aspect ratio problem caused by
  49. the VGA graphics adapter. The three corrected images will then be
  50. used to produce a single full color aspect ratio corrected image.
  51. The aspect ratio correction is performed in the function
  52. AspectCorrect.
  53. */
  54. #define ROWASPECTCORRECTION (double) 1.2
  55. #define SOURCEIMAGEROWS     240
  56.  
  57. /*
  58. Memory allocation bits used to keep track of how much far heap memory
  59. has been allocated by this program.
  60. */
  61.  
  62. #define  RGBMEM       1
  63. #define  REDMEM       2
  64. #define  GREENMEM     4
  65. #define  BLUEMEM      8
  66. #define  IMAGEMEM    16
  67.  
  68. unsigned MemoryAlloc = 0;              /* variable used to keep track */
  69.  
  70. #define COLLEVELS      32              /* number of brightness levels */
  71. #define MAXNUMCOLREGS 256              /* max num of color registers */
  72. #define NUMCOLS       320              /* image dimensions */
  73. #define NUMROWS       200
  74. #define NUMAXIS         3              /* num of axes in RGB cube */
  75. #define NUMPIXELS (long)NUMROWS*NUMCOLS /* total pixels in image */
  76. #define SQUARE(x) (x)*(x)
  77.  
  78. union REGS regs;
  79.  
  80. char huge *Red;                    /* pts to image data */
  81. char huge *Green;
  82. char huge *Blue;
  83. char huge *ImageBuf;
  84.  
  85. struct ImageReq Req;                   /* image request structure */
  86.  
  87. /*
  88. RGBCube: Three dimensional array implemented in such a way as to work well
  89. on a machine that has trouble with objects greater than 64K.  The indices
  90. into this array are the color components, rgb, normalized to fit in
  91. the range defined by COLLEVELS.  The values in the array are frequency counts
  92. of the particular color.  The array is set up here to be an array of pointers
  93. to smaller 2 dimensional arrays, so no object is greater than 64K.
  94. */
  95.  
  96. unsigned far *RGBCube[COLLEVELS];
  97. unsigned NumBoxes;
  98. unsigned FloydStein;
  99. unsigned OptimumColors;
  100. unsigned Verbose;
  101. unsigned GenComFile;
  102. unsigned GenPCXFile;
  103. unsigned ScaleColRegs;
  104. unsigned CorrectAspectRatio;
  105. /*
  106. Boxes: Structure holding the unsorted generated color boxes.  Includes the
  107. low and high value along each axis of RGBCube, and the number of elements
  108. in the box.
  109. */
  110.  
  111. struct Box
  112. {
  113.    unsigned Lo[3];
  114.    unsigned Hi[3];
  115.    unsigned long NumElements;
  116. }  Boxes[MAXNUMCOLREGS];
  117.  
  118. /*
  119. Sorted version of Boxes.
  120. */
  121.  
  122. struct Box SBoxes[MAXNUMCOLREGS];
  123.  
  124. /*
  125. ColRegs: Holds the determined values of the color registers.
  126. */
  127.  
  128. struct Regs
  129. {
  130.   BYTE Red;
  131.   BYTE Green;
  132.   BYTE Blue;
  133. } ColRegs[MAXNUMCOLREGS];
  134.  
  135. /*
  136. SColRegs: Sorted version of ColRegs.
  137. */
  138.  
  139. struct Regs SColRegs[MAXNUMCOLREGS];
  140.  
  141.  
  142. /* beginning of program functions */
  143.  
  144. /*
  145. This function will output the message string passed to it if the Verbose
  146. option variable is set true.
  147. */
  148.  
  149. void Message(char *String)
  150. {
  151.    if (Verbose)
  152.       printf("%s",String);
  153. }
  154.  
  155. /*
  156. This function deallocates all far heap memory that has been allocated
  157. */
  158.  
  159. void DeAllocMemory( void )
  160. {
  161.    register unsigned Index;
  162.  
  163.    /* test MemoryAlloc bit to see what was allocated then dispose */
  164.  
  165.    Message("Deallocating program memory\n");
  166.  
  167.    if (MemoryAlloc & RGBMEM)
  168.       for (Index=0; Index < COLLEVELS; Index++)
  169.          if (RGBCube[Index] != NULL)
  170.         farfree((unsigned far *) RGBCube[Index]);
  171.  
  172.    if (MemoryAlloc & REDMEM)
  173.       farfree((char far *) Red);
  174.    if (MemoryAlloc & GREENMEM)
  175.       farfree((char far *) Green);
  176.    if (MemoryAlloc & BLUEMEM)
  177.       farfree((char far *) Blue);
  178.    if (MemoryAlloc & IMAGEMEM)
  179.       farfree((char far *) ImageBuf);
  180. }
  181.  
  182. /*
  183. Allocate all memory for the entire program. The MemoryAlloc variable is
  184. used to keep track of all memory that has been allocated from the far
  185. heap. That way DeAllocMemory can give it all back to the system when the
  186. program terminates.
  187. */
  188.  
  189. unsigned AllocMemory( void )
  190. {
  191.    register unsigned Index;
  192.  
  193.    Message("Allocating program memory\n");
  194.  
  195.    /* create COLLEVELS number of pointer to 2 D arrays */
  196.  
  197.    /*
  198.    Set the initial values of the RGBCube sub-array pointers to NULL,
  199.    so we can do proper checks later to see if they have been allocated
  200.    or not.
  201.    */
  202.  
  203.    for (Index=0; Index < COLLEVELS; Index++)
  204.       RGBCube[Index] = NULL;
  205.  
  206.    for (Index=0; Index < COLLEVELS; Index++)
  207.    {
  208.       RGBCube[Index] = (unsigned far *) farcalloc(COLLEVELS*COLLEVELS,
  209.                        (unsigned long) sizeof(short));
  210.  
  211.       if (RGBCube[Index] == NULL)
  212.       {
  213.          printf("RGBCube memory allocation failed\n");
  214.          printf("\t only %ld bytes of memory available\n",farcoreleft());
  215.          DeAllocMemory();
  216.          return(FALSE);
  217.       }
  218.    }
  219.    MemoryAlloc |= RGBMEM;               /* indicate success */
  220.  
  221.    Red  = (char huge *) farcalloc(NUMPIXELS,(unsigned long) sizeof(char));
  222.    if (Red == NULL)
  223.    {
  224.       printf("Red allocation failed\n");
  225.       printf("\t only %ld bytes of memory available\n",farcoreleft());
  226.       DeAllocMemory();
  227.       return(FALSE);
  228.    }
  229.    MemoryAlloc |= REDMEM;              /* indicate success */
  230.  
  231.    Green = (char huge *) farcalloc(NUMPIXELS,(unsigned long) sizeof(char));
  232.    if (Green == NULL)
  233.    {
  234.       printf("Green allocation failed\n");
  235.       printf("\t only %ld bytes of memory available\n",farcoreleft());
  236.       DeAllocMemory();
  237.       return(FALSE);
  238.    }
  239.    MemoryAlloc |= GREENMEM;            /* indicate success */
  240.  
  241.    Blue = (char huge *) farcalloc(NUMPIXELS,(unsigned long) sizeof(char));
  242.    if (Blue == NULL)
  243.    {
  244.       printf("Blue allocation failed\n");
  245.       printf("\t only %ld bytes of memory available\n",farcoreleft());
  246.       DeAllocMemory();
  247.       return(FALSE);
  248.    }
  249.    MemoryAlloc |= BLUEMEM;            /* indicate success */
  250.  
  251.    /* allocate buffer into which all three images will be acquired */
  252.    ImageBuf = (char huge *) farcalloc(320L*240L,(unsigned long) sizeof(char));
  253.    if (ImageBuf == NULL)
  254.    {
  255.       printf("Image buffer allocation failed\n");
  256.       printf("\t only %ld bytes of memory available\n",farcoreleft());
  257.       DeAllocMemory();
  258.       return(FALSE);
  259.    }
  260.    MemoryAlloc |= IMAGEMEM;            /* indicate success */
  261.    return(TRUE);
  262. }
  263.  
  264.  
  265. /*
  266. This function forces the input data to fit in the range described by
  267. COLLEVELS.  When it is complete, the specified array will only contain
  268. values between 0 and COLLEVELS-1 inclusive.
  269. */
  270.  
  271. void Normalize(char huge *ColorData)
  272. {
  273.   register BYTE Min, Max;
  274.   unsigned long LongIndex;
  275.  
  276. /* find minimum and maximum values in the array. */
  277.  
  278.   Min = 0;
  279.   Max = 0;
  280.  
  281.   for (LongIndex=0; LongIndex < NUMPIXELS; LongIndex++)
  282.   {
  283.      if (ColorData[LongIndex] < Min)
  284.     Min = ColorData[LongIndex];
  285.      if (ColorData[LongIndex] > Max)
  286.     Max = ColorData[LongIndex];
  287.   }
  288.  
  289. /* Force each pixel to the range 0..COLLEVELS-1 */
  290.  
  291.   for (LongIndex=0; LongIndex < NUMPIXELS; LongIndex++)
  292.     ColorData[LongIndex] = ((ColorData[LongIndex]-Min)*(COLLEVELS-1))/
  293.                             (Max-Min);
  294. }
  295.  
  296. /*
  297. This function fills the RGBCube array with the color frequencies, and as a
  298. side effect, counts and prints the number of unique colors in the input.
  299. */
  300.  
  301. void ScanColorFrequencies( void )
  302. {
  303.    register unsigned c, k, NumColors;
  304.    unsigned long LongIndex;
  305.  
  306.    /* Initialize RGBCube to all zeros */
  307.  
  308.    for (c=0; c < COLLEVELS; c++)
  309.      for (k=0; k < COLLEVELS*COLLEVELS; k++)
  310.        RGBCube[c][k] = 0;
  311.  
  312.    /* For each pixel, count the number of pixels with that color */
  313.  
  314.    for (LongIndex=0; LongIndex < NUMPIXELS; LongIndex++)
  315.       RGBCube[Red[LongIndex]][(Blue[LongIndex]*COLLEVELS)+Green[LongIndex]]++;
  316.  
  317.    /*
  318.    Count and print the number of unique colors in the input by scanning the
  319.    RGBCube array and looking for non-zero frequencies.
  320.    */
  321.  
  322.    NumColors = 0;
  323.    for (c=0; c < COLLEVELS; c++)
  324.      for (k=0; k < COLLEVELS*COLLEVELS; k++)
  325.        if (RGBCube[c][k])
  326.      NumColors++;
  327.  
  328.    if (Verbose)
  329.       printf("%d unique colors in image data\n",NumColors);
  330. }
  331.  
  332. /*
  333. This function sets the indices to the numbers of the other axes after
  334. a main axis has been selected.
  335. */
  336.  
  337. void OtherAxes(unsigned mainaxis,unsigned *other1,unsigned *other2)
  338. {
  339.    switch (mainaxis)
  340.    {
  341.    case 0:
  342.      *other1 = 1;
  343.      *other2 = 2;
  344.      break;
  345.    case 1:
  346.      *other1 = 0;
  347.      *other2 = 2;
  348.      break;
  349.    case 2:
  350.      *other1 = 0;
  351.      *other2 = 1;
  352.    }
  353. }
  354.  
  355.  
  356. /*
  357. This function takes a index value into the Boxes array, and shrinks the
  358. specified box to tightly fit around the input color frequency data (eg.
  359. there are no zero planes on the sides of the box).
  360. */
  361.  
  362. void Shrink(unsigned BoxIndex)
  363. {
  364.    unsigned axis,aax1,aax2;
  365.    register unsigned ind[3], flag;
  366.  
  367.    /* Along each axis: */
  368.  
  369.    for (axis=0; axis < NUMAXIS; axis++)
  370.    {
  371.       OtherAxes(axis,&aax1,&aax2);
  372.  
  373.      /* Scan off zero planes on from the low end of the axis */
  374.  
  375.      flag = 0;
  376.      for (ind[axis]=Boxes[BoxIndex].Lo[axis];
  377.       ind[axis] <= Boxes[BoxIndex].Hi[axis]; ind[axis]++)
  378.      {
  379.         for (ind[aax1]=Boxes[BoxIndex].Lo[aax1];
  380.          ind[aax1] <= Boxes[BoxIndex].Hi[aax1]; ind[aax1]++)
  381.         {
  382.            for (ind[aax2]=Boxes[BoxIndex].Lo[aax2];
  383.         ind[aax2] <= Boxes[BoxIndex].Hi[aax2]; ind[aax2]++)
  384.               if (RGBCube[ind[0]][ind[1]*COLLEVELS+ind[2]])
  385.               {
  386.                  flag=1;
  387.                  break;
  388.               }
  389.        if (flag) break;
  390.         }
  391.         if (flag) break;
  392.      }
  393.      Boxes[BoxIndex].Lo[axis] = ind[axis];
  394.  
  395.      /* Scan off zero planes from the high end of the axis */
  396.      flag = 0;
  397.      for (ind[axis]=Boxes[BoxIndex].Hi[axis];
  398.       ind[axis]+1 >= Boxes[BoxIndex].Lo[axis]+1; ind[axis]--)
  399.      {
  400.         for (ind[aax1]=Boxes[BoxIndex].Hi[aax1];
  401.          ind[aax1]+1 >= Boxes[BoxIndex].Lo[aax1]+1; ind[aax1]--)
  402.         {
  403.            for (ind[aax2]=Boxes[BoxIndex].Hi[aax2];
  404.                 ind[aax2]+1>=Boxes[BoxIndex].Lo[aax2]+1; ind[aax2]--)
  405.               if (RGBCube[ind[0]][ind[1]*COLLEVELS+ind[2]])
  406.               {
  407.                  flag = 1;
  408.                  break;
  409.               }
  410.        if (flag) break;
  411.         }
  412.         if (flag) break;
  413.      }
  414.      Boxes[BoxIndex].Hi[axis] = ind[axis];
  415.    }
  416. }
  417.  
  418. /* print box debug function */
  419. void PrtBox(unsigned BoxIndex)
  420. {
  421.    printf("\nBox Number %d\n",BoxIndex);
  422.    printf("Hi[0]=%d Lo[0]=%d\n",Boxes[BoxIndex].Hi[0],Boxes[BoxIndex].Lo[0]);
  423.    printf("Hi[1]=%d Lo[1]=%d\n",Boxes[BoxIndex].Hi[1],Boxes[BoxIndex].Lo[1]);
  424.    printf("Hi[2]=%d Lo[2]=%d\n",Boxes[BoxIndex].Hi[2],Boxes[BoxIndex].Lo[2]);
  425.    printf("Elements %ld\n\n",Boxes[BoxIndex].NumElements);
  426.    getch();
  427. }
  428.  
  429.  
  430. /*
  431. This function selects the optimum colors from the color frequency data,
  432. using the Median Cut algorithm.  It prints the number of colors used at
  433. its termination.
  434. */
  435.  
  436. void SelectColorBoxes( void )
  437. {
  438.    register unsigned SelectedBox, c;
  439.    register unsigned ind[3], Max, axis, TargetBox, k;
  440.    unsigned aax1,aax2;
  441.    unsigned long LongMax, PlaneSum, ElementSum;
  442.  
  443.    /*
  444.    Initialize the first and only box in the array to contain the entire RGBCube,
  445.    then discard unused zero planes surrounding it.
  446.    */
  447.  
  448.    for (c=0; c < NUMAXIS; c++)
  449.    {
  450.       Boxes[0].Lo[c] = 0;
  451.       Boxes[0].Hi[c] = COLLEVELS-1;
  452.    }
  453.    Boxes[0].NumElements = NUMPIXELS;
  454.    NumBoxes = 1;
  455.  
  456.    Shrink(0);
  457.  
  458.    /* Perform the following until all color registers are used up */
  459.  
  460.    while(NumBoxes < MAXNUMCOLREGS)
  461.    {
  462.       /*
  463.       Pick the box with the maximum number of elements that is not a single
  464.       color value to work with.  It will be the box we will split.
  465.       */
  466.  
  467.       LongMax = 0;
  468.       SelectedBox = 1000;
  469.       for (c=0; c < NumBoxes; c++)
  470.       {
  471.      if ((Boxes[c].NumElements > LongMax) &&
  472.             ((Boxes[c].Lo[0] != Boxes[c].Hi[0]) ||
  473.              (Boxes[c].Lo[1] != Boxes[c].Hi[1]) ||
  474.              (Boxes[c].Lo[2] != Boxes[c].Hi[2])))
  475.          {
  476.         LongMax = Boxes[c].NumElements;
  477.             SelectedBox = c;
  478.          }
  479.       }
  480.  
  481.       /*
  482.       If we couldn't find any box that was not a single color, we don't
  483.       need to assign any more colors, so we can terminate this loop.
  484.       */
  485.  
  486.       if (SelectedBox == 1000)
  487.         break;
  488.  
  489.       /* Choose the longest axis of the box to split it along */
  490.  
  491.       axis = 0;
  492.       Max = Boxes[SelectedBox].Hi[axis] - Boxes[SelectedBox].Lo[axis];
  493.       for (k=1; k < NUMAXIS; k++)
  494.       {
  495.          if (Max < (c=(Boxes[SelectedBox].Hi[k]-Boxes[SelectedBox].Lo[k])))
  496.          {
  497.             Max = c;
  498.             axis = k;
  499.          }
  500.       }
  501.  
  502.       /*
  503.       Check to see if any of our previously assigned boxes have zero elements
  504.       (may happen in degenerate cases), if so, re-use them.  If not, use the
  505.       next available box.
  506.       */
  507.  
  508.       TargetBox = NumBoxes;
  509.       for (c=0; c < NumBoxes; c++)
  510.       {
  511.      if (Boxes[c].NumElements == 0)
  512.          {
  513.             TargetBox = c;
  514.             break;
  515.          }
  516.       }
  517.  
  518.       OtherAxes(axis,&aax1,&aax2);
  519.       if (Boxes[SelectedBox].Hi[axis] != Boxes[SelectedBox].Lo[axis])
  520.       {
  521.  
  522.          /*
  523.          Sum planes of box from low end until the sum exceeds half the total
  524.          number of elements in the box.  That is the point where we will
  525.          split it.
  526.          */
  527.  
  528.          ElementSum = 0;
  529.          for (ind[axis]=Boxes[SelectedBox].Lo[axis];
  530.           ind[axis] <= Boxes[SelectedBox].Hi[axis]; ind[axis]++)
  531.          {
  532.             PlaneSum = 0;
  533.             for (ind[aax1]=Boxes[SelectedBox].Lo[aax1];
  534.          ind[aax1] <= Boxes[SelectedBox].Hi[aax1]; ind[aax1]++)
  535.                for (ind[aax2]=Boxes[SelectedBox].Lo[aax2];
  536.             ind[aax2] <= Boxes[SelectedBox].Hi[aax2]; ind[aax2]++)
  537.                   PlaneSum += RGBCube[ind[0]][ind[1]*COLLEVELS+ind[2]];
  538.             ElementSum += PlaneSum;
  539.             if (ElementSum > Boxes[SelectedBox].NumElements/2)
  540.                break;
  541.          }
  542.          /*
  543.          If we did not exceed half the total until we added the last plane
  544.          (such as in a case where the last plane contains the bulk of the data
  545.          points), back up so we do not create the new box as a degenerate box.
  546.          */
  547.  
  548.      if (ind[axis] == Boxes[SelectedBox].Hi[axis])
  549.          {
  550.             ind[axis]--;
  551.             ElementSum -= PlaneSum;
  552.          }
  553.  
  554.          /*
  555.          The new box has most of the data the same as the old box, but its low
  556.          extent is the index above the point where we needed to split, and its
  557.          number of elements is the total number of elements in this whole box,
  558.          minus the number in the planes we just summed.
  559.          */
  560.  
  561.          for (c=0; c < NUMAXIS; c++)
  562.          {
  563.             Boxes[TargetBox].Lo[c] = Boxes[SelectedBox].Lo[c];
  564.             Boxes[TargetBox].Hi[c] = Boxes[SelectedBox].Hi[c];
  565.          }
  566.          Boxes[TargetBox].Lo[axis] = ind[axis]+1;
  567.          Boxes[TargetBox].NumElements = Boxes[SelectedBox].NumElements -
  568.                                         ElementSum;
  569.  
  570.          /*
  571.          The high extent of our old box is now cut off at the plane we just
  572.          split at and the number of elements in it is the number we just
  573.          summed.
  574.          */
  575.  
  576.          Boxes[SelectedBox].Hi[axis] = ind[axis];
  577.          Boxes[SelectedBox].NumElements = ElementSum;
  578.  
  579.          /* Discard zero planes around both our new boxes */
  580.  
  581.          Shrink(SelectedBox);
  582.          Shrink(TargetBox);
  583.  
  584.          /*
  585.          If we used the top box in our list, we have to increment the
  586.          total number of boxes used, to make ready for the use of the next
  587.          free box.
  588.          */
  589.  
  590.          if (TargetBox == NumBoxes)
  591.             NumBoxes++;
  592.       }
  593.    }
  594.  
  595.    /* show number of display colors to be used if requested to */
  596.    if (Verbose)
  597.       printf("%d colors will be used for display of the image\n",NumBoxes);
  598. }
  599.  
  600.  
  601. /*
  602. This function calculates the actual color register values for each box,
  603. based on the weighted distribution of data in the box.  It then sorts the
  604. color registers by brightness (using a calculation described by the VGA
  605. technical reference for calculating brightness).
  606. */
  607.  
  608. void SortColors( void )
  609. {
  610.    register unsigned Index,c,flag,temp,r,b,g;
  611.    unsigned indices[MAXNUMCOLREGS];
  612.    unsigned long weightedcolor[MAXNUMCOLREGS],rsum,bsum,gsum,tmp;
  613.  
  614.    for (Index=0; Index < NumBoxes; Index++)
  615.    {
  616.  
  617.       /* Calculate a weighted sum of the color values in the box */
  618.  
  619.       rsum = bsum = gsum = 0;
  620.       for (r=Boxes[Index].Lo[0]; r<=Boxes[Index].Hi[0]; r++)
  621.          for (b=Boxes[Index].Lo[1]; b<=Boxes[Index].Hi[1]; b++)
  622.             for (g=Boxes[Index].Lo[2]; g<=Boxes[Index].Hi[2]; g++)
  623.             {
  624.                tmp = RGBCube[r][b*COLLEVELS+g];
  625.                rsum += r*tmp;
  626.                bsum += b*tmp;
  627.                gsum += g*tmp;
  628.             }
  629.  
  630.       /* Pick the actual color for that box based on the the weighted sum */
  631.  
  632.       ColRegs[Index].Red   = rsum/Boxes[Index].NumElements;
  633.       ColRegs[Index].Blue  = bsum/Boxes[Index].NumElements;
  634.       ColRegs[Index].Green = gsum/Boxes[Index].NumElements;
  635.    }
  636.    /*
  637.    Set up for an index sort of the brightnesses by first calculating the
  638.    weighted brightness of each color (based on the calculation described in the
  639.    VGA manual.
  640.    */
  641.  
  642.    for (Index=0; Index < NumBoxes; Index++)
  643.    {
  644.       indices[Index] = Index;
  645.       weightedcolor[Index] = ColRegs[Index].Red  *30 +
  646.                  ColRegs[Index].Blue *11 +
  647.                  ColRegs[Index].Green*59;
  648.    }
  649.  
  650.    /*
  651.    Do a bubble sort of the weighted colors via indices. Sort is done in
  652.    ascending order.
  653.    */
  654.  
  655.    flag = 1;
  656.    while (flag)
  657.    {
  658.       flag = 0;
  659.       for (Index=0; Index < NumBoxes-1; Index++)
  660.          if (weightedcolor[indices[Index]] > weightedcolor[indices[Index+1]])
  661.          {
  662.             temp = indices[Index];
  663.             indices[Index] = indices[Index+1];
  664.             indices[Index+1] = temp;
  665.             flag = 1;
  666.          }
  667.    }
  668.  
  669.    /*
  670.    Re-map the boxes and the color registers into SBoxes and SColRegs via the
  671.    sorted indices found above.
  672.    */
  673.  
  674.    for (Index=0; Index < NumBoxes; Index++)
  675.    {
  676.       SColRegs[Index].Red   = ColRegs[indices[Index]].Red;
  677.       SColRegs[Index].Blue  = ColRegs[indices[Index]].Blue;
  678.       SColRegs[Index].Green = ColRegs[indices[Index]].Green;
  679.       SBoxes[Index].NumElements = Boxes[indices[Index]].NumElements;
  680.       for (c=0; c < NUMAXIS; c++)
  681.       {
  682.          SBoxes[Index].Hi[c] = Boxes[indices[Index]].Hi[c];
  683.          SBoxes[Index].Lo[c] = Boxes[indices[Index]].Lo[c];
  684.       }
  685.    }
  686. }
  687.  
  688. /* Get the color value of the specified pixel on VGA screen */
  689. unsigned GetPixelValue(unsigned long PixNum)
  690. {
  691.    register unsigned Col, Row;
  692.  
  693.    Col = (unsigned)(PixNum / (long) NUMROWS);
  694.    Row = (unsigned)(PixNum % (long) NUMROWS);
  695.    return(GetPixel256(Col,Row));
  696. }
  697.  
  698. /* Set the color value of the specified pixel on VGA screen */
  699. void SetPixelValue(unsigned long PixNum, unsigned Value)
  700. {
  701.    register unsigned Col, Row;
  702.  
  703.    Col = (unsigned)(PixNum / (long) NUMROWS);
  704.    Row = (unsigned)(PixNum % (long) NUMROWS);
  705.    PutPixel256(Col,Row,Value);
  706. }
  707.  
  708. /*
  709. This function maps the raw image pixel data in the input array into the
  710. new color map we've come up with in the SColRegs array.  In addition, it
  711. may use Floyd-Steinberg dithering to reduce the error in the image conversion.
  712. */
  713.  
  714. void DisplayImageData( void )
  715. {
  716.    register unsigned c,k,goodindex, PixVal;
  717.    unsigned long minerror,error,LongIndex;
  718.    register int RedDif,GreenDif,BlueDif,r,b,g,i;
  719.  
  720.    /*
  721.    Set the RGBCube array to a value that can't be a color register index
  722.    (MAXNUMCOLREGS*2) so we can detect when we hit on a part of the array that
  723.    is not included in a color box.
  724.    */
  725.  
  726.    for (c=0; c < COLLEVELS; c++)
  727.       for (k=0; k < COLLEVELS*COLLEVELS; k++)
  728.      RGBCube[c][k] = MAXNUMCOLREGS*2;
  729.  
  730.    /*
  731.    Fill the boxes in the RGBCube array with the index number for that box, so
  732.    we can tell what box a particular color index into the RGBCube array is in
  733.    by a single access.
  734.    */
  735.  
  736.    for (i=0; i < NumBoxes; i++)
  737.       for (r=SBoxes[i].Lo[0]; r <= SBoxes[i].Hi[0]; r++)
  738.      for (b=SBoxes[i].Lo[1]; b <= SBoxes[i].Hi[1]; b++)
  739.         for (g=SBoxes[i].Lo[2]; g <= SBoxes[i].Hi[2]; g++)
  740.                RGBCube[r][b*COLLEVELS+g] = i;
  741.  
  742.  
  743.  
  744.    /* for each pixel */
  745.  
  746.    for (LongIndex=0; LongIndex < NUMPIXELS; LongIndex++)
  747.    {
  748.       /*
  749.       If the color levels at that pixel are within proper range, and
  750.       this particular color is inside one of the boxes, and we are not
  751.       in optimum color mode, assign the color index for this pixel to the
  752.       value at that spot in the cube.
  753.       */
  754.  
  755.       if (Red[LongIndex]   < COLLEVELS &&
  756.           Blue[LongIndex]  < COLLEVELS &&
  757.           Green[LongIndex] < COLLEVELS &&
  758.           (RGBCube[Red[LongIndex]][Blue[LongIndex]*COLLEVELS+Green[LongIndex]]
  759.           !=MAXNUMCOLREGS*2) && !OptimumColors)
  760.       {
  761.          PixVal = RGBCube[Red[LongIndex]][Blue[LongIndex]*COLLEVELS+
  762.                           Green[LongIndex]];
  763.          SetPixelValue(LongIndex,PixVal);
  764.       }
  765.       else
  766.       {
  767.          /*
  768.          Otherwise, we need to scan the array of colors to find which is
  769.          the closest to our prospective color.
  770.          */
  771.  
  772.          goodindex = 0;
  773.          minerror = SQUARE(Red[LongIndex]-SColRegs[goodindex].Red)+
  774.                     SQUARE(Blue[LongIndex]-SColRegs[goodindex].Blue)+
  775.                     SQUARE(Green[LongIndex]-SColRegs[goodindex].Green);
  776.          /*
  777.          Scan all color registers to find which has the smallest error
  778.          when it is used for this pixel.
  779.          */
  780.  
  781.          for (k=1; k < NumBoxes; k++)
  782.          {
  783.             error = SQUARE(Red[LongIndex]-SColRegs[k].Red)+
  784.                     SQUARE(Blue[LongIndex]-SColRegs[k].Blue)+
  785.                     SQUARE(Green[LongIndex]-SColRegs[k].Green);
  786.             if (error < minerror)
  787.             {
  788.                minerror = error;
  789.                goodindex = k;
  790.             }
  791.          }
  792.          /* Assign that register to this pixel */
  793.          SetPixelValue(LongIndex,goodindex);
  794.       }
  795.  
  796.       /* do dithering if requested */
  797.  
  798.       if (FloydStein)
  799.       {
  800.          /*
  801.          Calculate the difference between the actual color at this pixel
  802.          and the color of the register we are assigning it to.
  803.          */
  804.          RedDif = ((int) SColRegs[GetPixelValue(LongIndex)].Red) -
  805.              ((int) Red[LongIndex]);
  806.          BlueDif = ((int) SColRegs[GetPixelValue(LongIndex)].Blue) -
  807.              ((int) Blue[LongIndex]);
  808.          GreenDif = ((int) SColRegs[GetPixelValue(LongIndex)].Green) -
  809.              ((int) Green[LongIndex]);
  810.  
  811.      /* If we are not at the right hand column of the image */
  812.  
  813.      if ((((LongIndex+NUMROWS) / NUMROWS) < NUMCOLS) &&
  814.            (LongIndex+NUMROWS) < NUMPIXELS)
  815.      {
  816.         /* Diffuse 3/8s of the error to the pixel to our right */
  817.  
  818.         Red[LongIndex+NUMROWS]   += (RedDif*3)/8;
  819.         Blue[LongIndex+NUMROWS]  += (BlueDif*3)/8;
  820.         Green[LongIndex+NUMROWS] += (GreenDif*3)/8;
  821.  
  822.         /* if that caused the pixel to our right to wrap */
  823.  
  824.         if (Red[LongIndex+NUMROWS]   > COLLEVELS-1 ||
  825.         Green[LongIndex+NUMROWS] > COLLEVELS-1 ||
  826.         Blue[LongIndex+NUMROWS]  > COLLEVELS-1)
  827.         {
  828.            /* undo the addition on that pixel */
  829.         Red[LongIndex+NUMROWS]   -= (RedDif*3)/8;
  830.         Blue[LongIndex+NUMROWS]  -= (BlueDif*3)/8;
  831.         Green[LongIndex+NUMROWS] -= (GreenDif*3)/8;
  832.         }
  833.      }
  834.      /* if not at bottom of image */
  835.      if (LongIndex % NUMROWS < NUMROWS-1)
  836.      {
  837.         /* Diffuse 3/8s of the error to the pixel below */
  838.         Red[LongIndex+1]   += (RedDif*3)/8;
  839.         Blue[LongIndex+1]  += (BlueDif*3)/8;
  840.         Green[LongIndex+1] += (GreenDif*3)/8;
  841.  
  842.         /*  If that caused the pixel below us to wrap */
  843.         if (Red[LongIndex+1]   > COLLEVELS-1 ||
  844.         Green[LongIndex+1] > COLLEVELS-1 ||
  845.         Blue[LongIndex+1]  > COLLEVELS-1)
  846.         {
  847.            /* Undo the addition on that pixel */
  848.            Red[LongIndex+1]   -= (RedDif*3)/8;
  849.            Blue[LongIndex+1]  -= (BlueDif*3)/8;
  850.            Green[LongIndex+1] -= (GreenDif*3)/8;
  851.         }
  852.      }
  853.      /* if not on last row and not in last column of image */
  854.      if ((((LongIndex+NUMROWS) / NUMROWS) < NUMCOLS) &&
  855.            (LongIndex % NUMROWS < NUMROWS-1))
  856.      {
  857.         /* Diffuse 1/4 of error to the pixel below and to our right */
  858.         Red[LongIndex+1+NUMROWS]   += RedDif/4;
  859.         Blue[LongIndex+1+NUMROWS]  += BlueDif/4;
  860.         Green[LongIndex+1+NUMROWS] += GreenDif/4;
  861.  
  862.         /* if pixel value wrapped */
  863.         if (Red[LongIndex+1+NUMROWS]   > COLLEVELS-1 ||
  864.         Green[LongIndex+1+NUMROWS] > COLLEVELS-1 ||
  865.         Blue[LongIndex+1+NUMROWS]  > COLLEVELS-1)
  866.         {
  867.            /* Undo the addition on that pixel */
  868.            Red[LongIndex+1+NUMROWS]   -= RedDif/4;
  869.            Blue[LongIndex+1+NUMROWS]  -= BlueDif/4;
  870.            Green[LongIndex+1+NUMROWS] -= GreenDif/4;
  871.         }
  872.      }
  873.       }
  874.    }
  875. }
  876.  
  877. /*
  878. The purpose of this function is to partially make up
  879. for the normalization of pixel values performed previously.
  880. The values in the color registers are scaled upwards toward
  881. the maximum value of 63 to increase image briteness.
  882. */
  883.  
  884. void ScaleColRegisters(void)
  885. {
  886.    unsigned Index;
  887.    unsigned MaxVal = 0;
  888.    unsigned Temp;
  889.  
  890.    /* Find the maximum value of any RGB component value */
  891.    for (Index = 0; Index < MAXNUMCOLREGS; Index++)
  892.    {
  893.       if (SColRegs[Index].Red > MaxVal)
  894.      MaxVal = SColRegs[Index].Red;
  895.       if (SColRegs[Index].Green > MaxVal)
  896.      MaxVal = SColRegs[Index].Green;
  897.       if (SColRegs[Index].Blue > MaxVal)
  898.      MaxVal = SColRegs[Index].Blue;
  899.    }
  900.    /* Scale all color register components accordingly */
  901.    for (Index = 0; Index < MAXNUMCOLREGS; Index++)
  902.    {
  903.       /* temp used to prevent overflow of BYTE value */
  904.       Temp = SColRegs[Index].Red * (unsigned) MAXCOLREGVAL;
  905.       Temp /= MaxVal;
  906.       SColRegs[Index].Red = Temp;
  907.  
  908.       Temp = SColRegs[Index].Green * (unsigned) MAXCOLREGVAL;
  909.       Temp /= MaxVal;
  910.       SColRegs[Index].Green = Temp;
  911.  
  912.       Temp = SColRegs[Index].Blue * (unsigned) MAXCOLREGVAL;
  913.       Temp /= MaxVal;
  914.       SColRegs[Index].Blue = Temp;
  915.    }
  916. }
  917.  
  918. void InstallPalette256( void )
  919. {
  920.    register unsigned Index;
  921.  
  922.    /*
  923.    With a graphics mode set, we can proceed to load our colors
  924.    into the DAC. A palette is not used in the 256 color mode of VGA.
  925.    */
  926.  
  927.    for (Index = 0; Index < MAXNUMCOLREGS; Index++)
  928.    {
  929.       /* set a Color Register */
  930.       regs.h.ah = 0x10;
  931.       regs.h.al = 0x10;
  932.       regs.x.bx = Index;
  933.       regs.h.dh = SColRegs[Index].Red;
  934.       regs.h.ch = SColRegs[Index].Green;
  935.       regs.h.cl = SColRegs[Index].Blue;
  936.       int86(VIDEO,®s,®s);
  937.    }
  938. }
  939.  
  940. /*
  941. This function produces an executable .COM file for display of the
  942. digitized color image. It writes a small code segment followed by
  943. the 256 color register value followed by the image data to the
  944. specified file.
  945. */
  946.  
  947. void WriteComFile(char *FileName)
  948. {
  949.    FILE *OutPutFile;
  950.    char     String[80];
  951.    unsigned Index, PixelValue, Col, Row;
  952.    BYTE FileCode[] =
  953.      {0xB4,0x0F,0xCD,0x10,0xA2,0x37,0x01,0xB4,0x00,0xB0,0x13,0xCD,0x10,
  954.       0xB8,0x12,0x10,0xBB,0x00,0x00,0xB9,0x00,0x01,0xBA,0x38,0x01,0xCD,
  955.       0x10,0xB9,0x00,0xFA,0xBE,0x38,0x04,0xB8,0x00,0xA0,0x8E,0xC0,0xBF,
  956.       0x00,0x00,0xF3,0xA4,0xB4,0x00,0xCD,0x16,0xB4,0x00,0xA0,0x37,0x01,
  957.       0xCD,0x10,0xC3,0x00};
  958.  
  959.  
  960.    if (!strchr(FileName,'.'))          /* is there an ext ? */
  961.    {
  962.       strcpy(String,FileName);         /* copy filename to buffer */
  963.       FileName = String;               /* FileName now pts at buffer */
  964.       strcat(FileName,".com");         /* if not add .com ext */
  965.    }
  966.     /* open the output file */
  967.  
  968.     if ((OutPutFile = fopen(FileName,"wb")) == NULL)
  969.     {
  970.        printf("Cannot open Image .COM file\n");
  971.        exit(1);
  972.     }
  973.  
  974.     /* write code segment to the file */
  975.     for (Index=0; Index < sizeof(FileCode); Index++)
  976.        if (fputc(FileCode[Index],OutPutFile) != FileCode[Index])
  977.        {
  978.           restorecrtmode();
  979.           DeAllocMemory();
  980.           printf("Error writing Image .COM code seg\n");
  981.           exit(1);
  982.        }
  983.  
  984.     /* now write the color register rgb values to the file */
  985.    for (Index = 0; Index < MAX256PALETTECOLORS; Index++)
  986.    {
  987.       if (fputc(SColRegs[Index].Red,OutPutFile) != SColRegs[Index].Red)
  988.       {
  989.          restorecrtmode();
  990.          DeAllocMemory();
  991.          printf("Error writing Image .COM red color reg\n");
  992.          exit(1);
  993.       }
  994.       if (fputc(SColRegs[Index].Green,OutPutFile) != SColRegs[Index].Green)
  995.       {
  996.          restorecrtmode();
  997.          DeAllocMemory();
  998.          printf("Error writing Image .COM green color reg\n");
  999.          exit(1);
  1000.       }
  1001.       if (fputc(SColRegs[Index].Blue,OutPutFile) != SColRegs[Index].Blue)
  1002.       {
  1003.          restorecrtmode();
  1004.          DeAllocMemory();
  1005.          printf("Error writing Image .COM blue color reg\n");
  1006.          exit(1);
  1007.       }
  1008.    }
  1009.    /* now write the actual image data to the file */
  1010.    for (Row=0; Row < NUMROWS; Row++)
  1011.       for (Col=0; Col < NUMCOLS; Col++)
  1012.       {
  1013.          PixelValue = GetPixel256(Col,Row); /* read the value from display */
  1014.          fputc(PixelValue,OutPutFile);
  1015.       }
  1016.  
  1017.    fclose(OutPutFile);
  1018. }
  1019.  
  1020. /*
  1021. This function corrects the aspect ratio distortion caused by
  1022. the 320x200 VGA display mode. Essentially, a 320x240 pixel image
  1023. is acquired by the digitizer and compressed into a 320x200 buffer.
  1024. See chapter 12 of this book for details.
  1025. */
  1026. void AspectCorrect(unsigned CorrectAspect, char huge *InImage,
  1027.                        char huge *OutImage)
  1028. {
  1029.    register unsigned Col, Row;
  1030.    register unsigned LowerBufferRow, UpperBufferRow;
  1031.    register unsigned UpperIntensity, LowerIntensity;
  1032.    register BYTE     Intensity;
  1033.    unsigned long     InImageBufOffset, OutImageBufOffset;
  1034.    double            FractionalRowAddr, RowDelta;
  1035.  
  1036.  
  1037.    if (CorrectAspect)
  1038.    {
  1039.       Message("Correcting Aspect Ratio of Image\n");
  1040.  
  1041.       /* For each column of the destination buffer (320 total) */
  1042.       for (Col=0; Col < LRMAXCOLS; Col++)
  1043.       {
  1044.      /*
  1045.      Calculate the start of the digitized video information
  1046.      in the image buffer for this column.
  1047.      */
  1048.      InImageBufOffset  = (long) SOURCEIMAGEROWS * Col;
  1049.      OutImageBufOffset = (long) LRMAXROWS * Col;
  1050.      /*
  1051.      For each rows in the destination buffer ...
  1052.      */
  1053.      for (Row=0; Row < LRMAXROWS; Row++)
  1054.      {
  1055.         /*
  1056.         Which actual digitized video row out of the
  1057.         total of 240 should we accessed ? The calculated
  1058.         address will reside between two actual addresses.
  1059.         The address will be fractional.
  1060.         */
  1061.         FractionalRowAddr = ROWASPECTCORRECTION * (double) Row;
  1062.         /*
  1063.         Get the address of the row bytes just below and just
  1064.         above the calculated fractional address. Fetch the
  1065.         intensity values of each.
  1066.         */
  1067.         LowerBufferRow = (unsigned) FractionalRowAddr;
  1068.         UpperBufferRow = LowerBufferRow + 1;
  1069.         LowerIntensity = InImage[InImageBufOffset + LowerBufferRow];
  1070.         UpperIntensity = InImage[InImageBufOffset + UpperBufferRow];
  1071.  
  1072.         /*
  1073.         Calculate the distance the fractional address is off
  1074.         from the lower real address. This distance is required
  1075.         for the interpolation process.
  1076.         */
  1077.         RowDelta = FractionalRowAddr - LowerBufferRow;
  1078.         /*
  1079.         Interpolate for the value of the intensity to assign
  1080.         to the pixel at this row.
  1081.         */
  1082.         Intensity = RowDelta*((double) UpperIntensity - LowerIntensity) +
  1083.                        LowerIntensity;
  1084.  
  1085.         /*
  1086.         Store the calculated intensity in the destination
  1087.         image buffer;
  1088.         */
  1089.         OutImage[OutImageBufOffset+Row] = Intensity;
  1090.      }
  1091.       }
  1092.    }
  1093. }
  1094.  
  1095.  
  1096.  
  1097. /*
  1098. This function provides help in the advent of operator error. Program
  1099. terminates after help is given
  1100. */
  1101.  
  1102. void ShowHelp( void )
  1103. {
  1104.    printf("\nThis program digitizes and displays a color image. It\n");
  1105.    printf("is envoked as follows:\n\n");
  1106.    printf("Usage: cvideo [-a -d -h -o -p -s -v -x] filename <cr>\n");
  1107.    printf("  -a correct aspect ratio of image\n");
  1108.    printf("  -d use Floyd-Steinberg dithering\n");
  1109.    printf("  -h or ? show help\n");
  1110.    printf("  -o calculates optimum color palette\n");
  1111.    printf("  -p create PCX output file\n");
  1112.    printf("  -s scales color register values to max\n");
  1113.    printf("  -v displays program progress information\n");
  1114.    printf("  -x create executable display program\n");
  1115.    printf("  filename is name given to generated display file(s).\n");
  1116.    printf("     Do not specify a file extension, it will be provided.\n\n");
  1117.    exit(1);
  1118. }
  1119.  
  1120. /* main digitizer program */
  1121.  
  1122. void main(short argc, char *argv[])
  1123. {
  1124.    unsigned FileNameCounter, ArgIndex;
  1125.    char    *ImageFileName;
  1126.  
  1127.  
  1128.    InitGraphics();
  1129.  
  1130.    clrscr();
  1131.    printf("Digitize, Display and Save a Color Image\n\n");
  1132.  
  1133.    /* install default options */
  1134.    FloydStein    = FALSE;              /* no dithering */
  1135.    OptimumColors = FALSE;              /* use best guess color approx */
  1136.    Verbose       = FALSE;              /* don't be wordy */
  1137.    GenComFile    = FALSE;              /* don't generate an .COM file */
  1138.    GenPCXFile    = FALSE;              /* don't generate a. PCX file */
  1139.    ScaleColRegs  = FALSE;              /* don't scale col reg values */
  1140.    CorrectAspectRatio = FALSE;         /* don't correct aspect ratio */
  1141.  
  1142.    /* parse all command line arguments */
  1143.  
  1144.    FileNameCounter = 0;                /* count of user specified filenames */
  1145.    for (ArgIndex=1; ArgIndex < argc; ArgIndex++)
  1146.    {
  1147.       if (*argv[ArgIndex] != '-')      /* if not a cmd line switch */
  1148.       {                                /* must be a filename */
  1149.      if (*argv[ArgIndex] == '?')   /* help requested ? */
  1150.         ShowHelp();
  1151.      if (FileNameCounter > 1)      /* only one filename allowed */
  1152.             ShowHelp();                /* if more then error exit */
  1153.      ImageFileName = argv[ArgIndex];  /* save image filename */
  1154.          FileNameCounter++;            /* inc count for error check */
  1155.       }
  1156.       else                             /* its a cmd line switch */
  1157.       {
  1158.          switch (*(argv[ArgIndex]+1))     /* parse the cmd line */
  1159.          {
  1160.         case 'a':
  1161.         case 'A':
  1162.           CorrectAspectRatio = TRUE;
  1163.               break;
  1164.         case 'd':
  1165.         case 'D':
  1166.           FloydStein = TRUE;
  1167.               break;
  1168.         case 'h':
  1169.         case 'H':
  1170.           ShowHelp();
  1171.               break;
  1172.         case 'o':
  1173.         case 'O':
  1174.           OptimumColors = TRUE;
  1175.           break;
  1176.         case 'p':
  1177.         case 'P':
  1178.           GenPCXFile = TRUE;
  1179.               break;
  1180.         case 's':
  1181.         case 'S':
  1182.           ScaleColRegs = TRUE;
  1183.               break;
  1184.             case 'v':
  1185.             case 'V':
  1186.           Verbose    = TRUE;
  1187.               break;
  1188.         case 'x':
  1189.         case 'X':
  1190.           GenComFile = TRUE;
  1191.               break;
  1192.         default:
  1193.           printf("Error - invalid cmd line switch encountered\n");
  1194.           ShowHelp();
  1195.          }
  1196.       }
  1197.    }
  1198.    if ((GenComFile | GenPCXFile) && (FileNameCounter != 1))
  1199.    {
  1200.       printf("Error - single filename required for output file(s)\n");
  1201.       ShowHelp();
  1202.    }
  1203.  
  1204.  
  1205.    /* attempt to allocate all required memory */
  1206.    if (!AllocMemory())
  1207.    {
  1208.       printf("memory allocation error - program terminated !\n");
  1209.       exit(ENoMemory);
  1210.    }
  1211.  
  1212.    /*
  1213.    Build the structure that defines what the digitizer should acquire. This
  1214.    will be passed to the digitizer by a call to InitializeDigitizer
  1215.    function
  1216.    */
  1217.  
  1218.    Req.ComputerType   = PS220;
  1219.    Req.PrtBase        = LPT1;
  1220.    Req.HMode          = LowRes;
  1221.    Req.VMode          = NonInterlace;
  1222.    Req.NumberOfPasses = 1;
  1223.    Req.Flags          = 0L;
  1224.    Req.FirstLine      = 0;
  1225.    Req.FirstPixel     = 0;
  1226.    Req.LastPixel      = 320;
  1227.  
  1228.    /* acquire a 320x240 image if aspect ratio is to be corrected */
  1229.    if (CorrectAspectRatio)
  1230.       Req.LastLine       = 240;
  1231.    else
  1232.       Req.LastLine       = 200;
  1233.  
  1234.  
  1235.    InitializeDigitizer(&Req);          /* tell hardware what to do */
  1236.  
  1237.    printf("Put Red filter in front of the camera\n");
  1238.    printf("Press a key when ready\n");
  1239.    getch();
  1240.    Message("Acquiring the Red Picture Data from the Digitizer\n");
  1241.    if (CorrectAspectRatio)
  1242.       Req.PictBuf = ImageBuf;
  1243.    else
  1244.       Req.PictBuf = Red;
  1245.    GetPicture();
  1246.    AspectCorrect(CorrectAspectRatio,ImageBuf,Red);
  1247.  
  1248.    printf("Put Green filter in front of the camera\n");
  1249.    printf("Press a key when ready\n");
  1250.    getch();
  1251.    Message("Acquiring the Green Picture Data from the Digitizer\n");
  1252.    if (CorrectAspectRatio)
  1253.       Req.PictBuf = ImageBuf;
  1254.    else
  1255.       Req.PictBuf = Green;
  1256.    GetPicture();
  1257.    AspectCorrect(CorrectAspectRatio,ImageBuf,Green);
  1258.  
  1259.    printf("Put Blue filter in front of the camera\n");
  1260.    printf("Press a key when ready\n");
  1261.    getch();
  1262.    Message("Acquiring the Blue Picture Data from the Digitizer\n");
  1263.    if (CorrectAspectRatio)
  1264.       Req.PictBuf = ImageBuf;
  1265.    else
  1266.       Req.PictBuf = Blue;
  1267.    GetPicture();
  1268.    AspectCorrect(CorrectAspectRatio,ImageBuf,Blue);
  1269.  
  1270.    Message("Picture data acquired. Processing the palette data ...\n");
  1271.  
  1272.    Message("Normalizing color data\n");
  1273.    Normalize(Red);
  1274.    Normalize(Blue);
  1275.    Normalize(Green);
  1276.  
  1277.    Message("Building color frequency table\n");
  1278.    ScanColorFrequencies();
  1279.  
  1280.    Message("Selecting optimum color palette\n");
  1281.    SelectColorBoxes();
  1282.    SortColors();
  1283.  
  1284.    /* display the resultant color image with the proper palette */
  1285.    Set256ColorMode();
  1286.    DisplayImageData();
  1287.  
  1288.    if (ScaleColRegs)
  1289.       ScaleColRegisters();
  1290.  
  1291.    InstallPalette256();
  1292.  
  1293.    if (GenPCXFile)
  1294.    {
  1295.       /* create a PCX file for display of color image */
  1296.       WritePCXFile(ImageFileName,8,320,200,1,320);
  1297.    }
  1298.  
  1299.    if (GenComFile)
  1300.    {
  1301.       /* create an executable file for display of color image */
  1302.       WriteComFile(ImageFileName);
  1303.    }
  1304.  
  1305.    /* prepare to return to dos */
  1306.    getch();
  1307.    restorecrtmode();
  1308.    DeAllocMemory();
  1309.    closegraph();
  1310. }
  1311.  
  1312.