home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: Science / Science.zip / imdisp79.zip / IMAGUTIL.C < prev    next >
C/C++ Source or Header  |  1993-04-17  |  22KB  |  687 lines

  1. /***  IMDISP module IMAGUTIL.C
  2.  
  3.       Special Purpose Device Independant Display Routines
  4.  
  5.     Contains high level display routines such as interactive palette
  6.     adjustment, histogram display, and image profile drawing.
  7. ***/
  8.  
  9. #define __MSC
  10.  
  11. /* Changed 10/6/87 to read cursor keys without numlock - mdm */
  12.  
  13. /* * * * INCLUDE files * * * */
  14.  
  15. #include  <math.h>
  16. #include  <stdio.h>
  17. #include  <conio.h>
  18. #include  "imdef.h"
  19. #include  "imdisp.h"
  20. #include  "dispio.h"
  21. #include  "disputil.h"
  22. #include  "display.h"
  23. #include  "keywutil.h"
  24. #include  "palutil.h"
  25. #include  "plot.h"
  26. #include  "refresh.h"
  27. #include  "textutil.h"
  28.  
  29. /* * * * External functions * * * */
  30.  
  31. /* * * * Function declarations * * * */
  32.  
  33. int  DisplayHistogram (long *,int ,int ,int );
  34. int  Stretch (int ,int );
  35. int  Profile (void);
  36. void DoNegative(void);
  37. void DoWindow(void);
  38. void DoRotate(void);
  39.  
  40. /* * * * Global Variables * * * */
  41.  
  42.    long sin_table[MAXDISPNS+400];
  43.    long cos_table[MAXDISPNS+400];
  44.  
  45.  
  46.  
  47.  
  48. int DisplayHistogram (long int * histbuf, int numbins, int minDN, int maxDN)
  49.  
  50. /***  DisplayHistogram diplays the passed histogram on the screen.
  51.     The third highest bin in the histogram is used to scale the
  52.     plot.  The axes of the plot are labeled and annotated.  The
  53.     number in each bin is indicated by the length of the line
  54.     drawn vertically from the axis.
  55.  
  56.      Parameter  Type            Description
  57.       histbuf   long int array  the array of count values for each bin
  58.       numbins   int             the number of bins in the histogram
  59.       minDN     int             the minimum DN value in the histogram
  60.       maxDN     int             the maximum DN value in the histogram
  61.  
  62. ***/
  63.  
  64. {
  65.     int     top, bottom, left, right, height, pixperbin, width;
  66.     int     i, DN, j, length, tic, tic_height=5;
  67.     int     num_Xtics=16, num_Ytics=10, num_Xlabels=8, num_Ylabels=5;
  68.     long    count, MaxCount;
  69.     char    dispstring[16];
  70.  
  71.  
  72.     top       = dispnl / 10;
  73.     bottom    = dispnl - top;
  74.     left      = dispns / 10;
  75.     right     = dispns - (dispns / 100);
  76.     height    = bottom - top;
  77.     pixperbin = (right - left) / numbins;
  78.     width     = (numbins+1)*pixperbin;
  79.     right     = left + width - 1;            /* reset right edge */
  80.  
  81.  
  82.     MaxCount = max3arr( histbuf, numbins);
  83.  
  84. /* Draw the x axis  (DN value) */
  85. /*    DrawLine (bottom, left, bottom, right, numDN-1); */
  86.     FrameBox( top, left, bottom, right, numDN-1, FALSE);
  87.     tic = (numbins / 16) * pixperbin;
  88.     tic_height = dispnl / 100;
  89. /* Put in the tic marks */
  90.     for (i = 0;  i <= num_Xtics;  i++)
  91.     {
  92.         j = left + pixperbin + i*tic;
  93.         DrawLine (bottom, j, bottom+tic_height, j, numDN-1);
  94.     }
  95. /* Put in the labels */
  96.     for (i = 0;  i <= num_Xlabels;  i++)
  97.     {
  98.         DN = i*0.125*((float)maxDN-minDN+1) + (float)minDN + 0.5;
  99.         if (DN < 0)  DN--;
  100.         sprintf (dispstring, "%3d", DN);
  101.         j = left + (((numbins*i) / 8) + 1)*pixperbin - 15;
  102.         DrawText (dispstring, bottom+20, j, SmallChars, 0, numDN-1);
  103.     }
  104. /* And write out the X-axis title */
  105.     DrawText ("DN value", bottom+40, left+(width / 2) - 75, TextHeight, 0, numDN-1);
  106.  
  107.  
  108. /* Draw the y axis  (Counts) */
  109. /*    DrawLine (bottom, left, top, left, numDN-1); */
  110.     for (i = 0;  i <= num_Ytics;  i++)
  111.     {
  112.         j = bottom - height*i/num_Ytics;
  113.         DrawLine (j, left, j, left-tic_height, numDN-1);
  114.     }
  115.     DrawText ("0", bottom-5, left-20, SmallChars, 0, numDN-1);
  116.     sprintf (dispstring, "%5ld", MaxCount);
  117.     DrawText (dispstring, bottom-height+5, left-60, SmallChars, 0, numDN-1);
  118.     DrawText ("Counts", bottom-(height / 2)+50, left-20, TextHeight, 90, numDN-1);
  119.  
  120.  
  121.  
  122. /* Draw the histogram lines */
  123.     j = left;
  124.     for (i = 0;  i < numbins;  i++)
  125.     {
  126.         j += pixperbin;
  127.         if (histbuf[i] > MaxCount)
  128.             length = height;
  129.         else
  130.             length = height*((float)histbuf[i]/(float)MaxCount) + 0.5;
  131.  
  132.         if (Color_Hist)
  133.           DrawLine (bottom, j, bottom-length, j, i); /* color histo */
  134.         else
  135.           DrawLine (bottom, j, bottom-length, j, numDN-1);
  136.     }
  137.  
  138. }
  139.  
  140. int Stretch (int DNlow, int DNhigh)
  141.  
  142. /***  Stretch applies a linear gray scale ramp to the palette
  143.     starting with 0 at Dnlow and proceeding to 255 at DNhigh.
  144.  
  145.      Parameter  Type   Description
  146.       DNlow     int    the starting DN value of the linear ramp
  147.       DNhigh    int    the ending DN value of the linear ramp
  148.  
  149. ***/
  150. /* int   DNlow, DNhigh; */
  151. {
  152.     Color  CT[256];
  153. /*    struct  Color  CT[256]; */
  154.     float   slope;
  155.     int     i, shade;
  156.  
  157.  
  158.     if (DNhigh > numDN-1)   DNhigh = numDN-1;
  159.     if (DNhigh < 0)   DNhigh = numDN-1;
  160.     if (DNlow < 0)   DNlow = 0;
  161.     if (DNlow > numDN-1)   DNlow = 0;
  162.     slope = 256/ (float)(DNhigh - DNlow + 1);
  163.     for (i = 0;  i < numDN;  i++)
  164.     {
  165.         shade = slope*(i-DNlow) + 0.5;
  166.         if (shade < 0)   shade = 0;
  167.         if (shade > 255) shade = 255;
  168.         CT[i].r = shade;
  169.         CT[i].g = shade;
  170.         CT[i].b = shade;
  171.     }
  172.     WritePalette (CT);
  173. }
  174.  
  175. int Profile( void )
  176.  
  177. /***  Profile plots a profile of the pixel values between two points
  178.     on the screen.  Cursor mode is used to select the two endpoints.
  179.     A line is drawn between the endpoints and a plot of DN value
  180.     versus distance along the line is made.
  181. ***/
  182. {
  183.     int  line1, samp1, line2, samp2;
  184.     int  line, samp, distance, delline, delsamp;
  185.     int  bottom, left, height, width, top, right;
  186.     int  DN,  minDN, maxDN;
  187.     int  i, j, numpoints;
  188.     float  t, scalefact;
  189.     char   dispstring[64];
  190.     int    DNvalues[MAXDISPNL+MAXDISPNS];
  191.     int    Pixels[MAXDISPNL+MAXDISPNS];
  192.     unsigned char  SavePatch[CURSORSIZE][CURSORSIZE];
  193.  
  194.  
  195.     MoveCursor (&line1, &samp1);
  196.     PlaceCursor (line1, samp1, numDN-1);   CursorOn = 0;
  197.     for (i = 0;  i < CURSORSIZE; i++)
  198.         for (j = 0;  j < CURSORSIZE; j++)
  199.             SavePatch[i][j] = CursorPatch[i][j];
  200.     MoveCursor (&line2, &samp2);
  201.     for (i = 0;  i < CURSORSIZE; i++)
  202.         for (j = 0;  j < CURSORSIZE; j++)
  203.             CursorPatch[i][j] = SavePatch[i][j];
  204.     CursorOn = 1;   CursorLine = line1;  CursorSample = samp1;
  205.     RemoveCursor();
  206.     if (line1==line2 && samp1==samp2) return(0);
  207.     delline = line2 - line1;   delsamp = samp2 - samp1;
  208.     distance = sqrt(  (float)
  209.                ( (long)delline*delline + (long)delsamp*delsamp ) ) + 0.5;
  210.  
  211.  
  212.     bottom = dispnl - 45;      left = 60;
  213.     height = dispnl/3;         width = dispns - 150;
  214.     top = bottom - height;     right = left + width;
  215.  
  216.     minDN = 255;    maxDN = 0;
  217.     numpoints = distance;
  218.     if (numpoints > width / 2)   numpoints = width / 2;
  219.     for (i = 0;  i <= numpoints;  i++)
  220.     {
  221.         t = (float)i/numpoints;
  222.         line = line1 + (int)(delline*t+0.5);
  223.         samp = samp1 + (int)(delsamp*t+0.5);
  224.         ReadPixel (line, samp, &DN);
  225.         if (DN < minDN)   minDN = DN;
  226.         if (DN > maxDN)   maxDN = DN;
  227.         DNvalues[i] = DN;
  228.         Pixels[i] = (int)(width*t+0.5) + left;
  229.     }
  230.  
  231.     if (minDN == maxDN)   scalefact = 0;
  232.       else scalefact = (float)height/(maxDN - minDN);
  233.     for (i = 0;  i<= numpoints;  i++)
  234.         DNvalues[i] = bottom - (int)( scalefact*(DNvalues[i] - minDN) + 0.5);
  235.  
  236.  
  237.     DrawLine (line1, samp1,  line2, samp2, numDN-1);
  238.  
  239.           /* Draw the x axis  (Pixels) */
  240.     DrawLine (bottom, left, bottom, right, numDN-1);
  241.     DrawLine (bottom, right, bottom-5, right, numDN-1);
  242.     DrawText ("0", bottom+20, left, SmallChars, 0, numDN-1);
  243.     sprintf (dispstring, "%4d", distance);
  244.     DrawText (dispstring, bottom+20, right-30, SmallChars, 0, numDN-1);
  245.     DrawText ("Pixels", bottom+25, left+(width/2) - 70, TextHeight, 0, numDN-1);
  246.  
  247.  
  248.           /* Draw the y axis  (DN value) */
  249.     DrawLine (bottom, left, top, left, numDN-1);
  250.     DrawLine (top, left, top, left+5, numDN-1);
  251.     sprintf (dispstring, "%3d", minDN);
  252.     DrawText (dispstring, bottom-5, left-40, SmallChars, 0, numDN-1);
  253.     sprintf (dispstring, "%3d", maxDN);
  254.     DrawText (dispstring, top+5, left-40, SmallChars, 0, numDN-1);
  255.     DrawText ("DN Value", bottom-(height / 2)+50, left-30, TextHeight, 90, numDN-1);
  256.  
  257.  
  258.     for (i = 0;  i < numpoints; i++)
  259.         DrawLine (DNvalues[i], Pixels[i], DNvalues[i+1], Pixels[i+1], numDN-1);
  260.  
  261. }
  262.  
  263. void DoNegative(void)
  264. {
  265.     int i;
  266.     unsigned char hold;
  267.     Color  coltab[256];
  268. /*    struct Color  coltab[256]; */
  269.  
  270.     ReadPalette(coltab);
  271.  
  272.     for (i = 1; i <= numDN/2;  i++)
  273.     {    hold               = coltab[numDN-i].r;
  274.          coltab[numDN-i].r  = coltab[i-1].r;
  275.          coltab[i-1].r      = hold;
  276.  
  277.          hold               = coltab[numDN-i].g;
  278.          coltab[numDN-i].g  = coltab[i-1].g;
  279.          coltab[i-1].g      = hold;
  280.  
  281.          hold               = coltab[numDN-i].b;
  282.          coltab[numDN-i].b  = coltab[i-1].b;
  283.          coltab[i-1].b      = hold;
  284.     }
  285.  
  286.     WritePalette(coltab);
  287. }
  288.  
  289.  
  290. void DoWindow( void )
  291. /* DoWindow - This routine will allow the user to select an area using the
  292.               cursor keys.  A box is stretched (or rubberbanded) around
  293.               as the user moves the cursor around.  The area is retained
  294.               to be either stored in a buffer or have filters applied to.
  295.  
  296.     Written by Ron Baalke - 09/17/91
  297. */
  298. {
  299.     int  line1, samp1, line2, samp2;
  300.     int  bottom, left, height, width, top, right;
  301.     char   dummy[80];
  302.     unsigned char  SavePatch[CURSORSIZE][CURSORSIZE];
  303.     int flag1, flag2;
  304.     int i,j;
  305.  
  306.     GetKeywordString (CommandString, "CAN", "",dummy, &flag1);
  307.     if (flag1 != -1)
  308.     {
  309.        Image_Line      = Image_Line1;        /* Restore image coordinates */
  310.        Image_Sample    = Image_Sample1;      /* Ron Baalke - 09/18/91 */
  311.        Image_Height    = Image_Height1;
  312.        Image_Length    = Image_Length1;
  313.        Image_Zoom      = Image_Zoom1;
  314.        Image_Subsample = Image_Subsample1;
  315.        return;
  316.     }
  317.  
  318.     GetKeywordInteger (CommandString, "L1", 1, &line1, &flag1);
  319.     GetKeywordInteger (CommandString, "S1", 1, &samp1, &flag2);
  320.     if ((flag1 != -1) && (flag2 == -1))
  321.     {
  322.        if (line1 > dispnl) line1 = dispnl;
  323.        if (samp1 > dispns) samp1 = dispns;
  324.     }
  325.     else
  326.     {
  327.        MoveCursor (&line1, &samp1);
  328.        PlaceCursor (line1, samp1, numDN-1);   CursorOn = 0;
  329.        for (i = 0;  i < CURSORSIZE; i++)
  330.            for (j = 0;  j < CURSORSIZE; j++)
  331.                SavePatch[i][j] = CursorPatch[i][j];
  332.     }
  333.  
  334.     GetKeywordInteger (CommandString, "L2", 1, &line2, &flag1);
  335.     GetKeywordInteger (CommandString, "S2", 1, &samp2, &flag2);
  336.     if ((flag1 != -1) && (flag2 == -1))
  337.     {
  338.        if (line2 > dispnl) line2 = dispnl;
  339.        if (samp2 > dispns) samp2 = dispns;
  340.     }
  341.     else
  342.     {
  343.        RubberBand (line1, samp1, &line2, &samp2);
  344.        for (i = 0;  i < CURSORSIZE; i++)
  345.            for (j = 0;  j < CURSORSIZE; j++)
  346.                CursorPatch[i][j] = SavePatch[i][j];
  347.        CursorOn = 1;   CursorLine = line1;  CursorSample = samp1;
  348.        RemoveCursor();
  349.     }
  350.  
  351.     if (line1==line2 || samp1==samp2) return;
  352.  
  353.     if (line1 < line2)
  354.     {
  355.        top = line1;
  356.        bottom = line2;
  357.     }
  358.     else
  359.     {
  360.        top = line2;
  361.        bottom = line1;
  362.     }
  363.  
  364.     if (samp1 < samp2)
  365.     {
  366.        left = samp1;
  367.        right = samp2;
  368.     }
  369.     else
  370.     {
  371.        left = samp2;
  372.        right = samp1;
  373.     }
  374.  
  375.     FrameBox(top,left,bottom,right,numDN-1,TRUE);
  376.  
  377.     /* save the dimensions of the box */
  378.  
  379.     Image_Line = top+1;
  380.     Image_Sample = left+1;
  381.     Image_Height = bottom - top - 1;
  382.     Image_Length = right - left - 1;
  383.     Image_Zoom = 1;
  384.     Image_Subsample = 1;
  385. }
  386.  
  387. /*************************************************************************/
  388. /* DoRotate                                                              */
  389. /*                                                                       */
  390. /* Written by Ron Baalke - 09/21/91                                      */
  391. /*                                                                       */
  392. /* This routine will rotate an image between 0 to 360 degrees with an    */
  393. /* accuracy of 1 degree.  Options are provided to clip the image in its  */
  394. /* original box, and to not fill in the dead areas around the image.     */
  395. /* The image is retrieved pixel-by-pixel from the refresh buffer, so the */
  396. /* refresh buffer must be turned on for this routine to work             */
  397. /*************************************************************************/
  398.  
  399. void DoRotate(void)
  400. {
  401.  
  402.    int i,j,k;
  403.    int flag;
  404.    int clip_flag;
  405.    int nofill_flag;
  406.    int line;
  407.    int sample;
  408.    int height;
  409.    int length;
  410.    int angle;
  411.    int line_center;
  412.    int samp_center;
  413.    unsigned char buffer1[MAXDISPNS];
  414.    unsigned char buffer2[MAXDISPNS];
  415.    int save;
  416.    long cos_angle;
  417.    long sin_angle;
  418.    double dangle;
  419.    int line_diff;
  420.    int samp_diff;
  421.    long line_pixel;
  422.    long samp_pixel;
  423.    int skip;
  424.    int line_start;
  425.    int line_end;
  426.    int samp_start;
  427.    int samp_end;
  428.    int l1,s1,l2,s2,l3,s3,l4,s4;
  429.    int  offset;
  430.  
  431.    GetKeywordSubcommand (CommandString, "SCR", &flag);
  432.    GetKeywordSubcommand (CommandString, "CLI", &clip_flag);
  433.    GetKeywordSubcommand (CommandString, "NOF", &nofill_flag);
  434.  
  435.    /* Rotate just the image or the entire screen */
  436.  
  437.    if (flag == 1)
  438.    {
  439.       line = 1;
  440.       sample = 1;
  441.       height = dispnl;
  442.       length = dispns;
  443.    }
  444.    else
  445.    {
  446.       line   = Image_Line;
  447.       sample = Image_Sample;
  448.       height = Image_Height;
  449.       length = Image_Length;
  450.    }
  451.  
  452.    /* Error checking - angle must be between 0 to 360 degrees */
  453.  
  454.    GetKeywordInteger (CommandString, "ROT", 0, &angle, &flag);
  455.  
  456.    if (angle == 0 || angle == 360) return;
  457.    if (angle < 0  || angle >  360)
  458.    {
  459.       StatusLine(0,"Angle of rotation must be between 0 and 360");
  460.       return;
  461.    }
  462.  
  463.    /* Refresh buffer must be turned on */
  464.  
  465.    if (RefreshLines == 0)
  466.    {
  467.       StatusLine(0,"Refresh Buffer must be turned on to do rotations");
  468.       return;
  469.    }
  470.  
  471.    /* calculate center, starting and ending points of the image */
  472.  
  473.    line_center = line +   height/2;
  474.    samp_center = sample + length/2;
  475.    line_start = line;
  476.    line_end = line+height-1;
  477.    samp_start = sample;
  478.    samp_end = sample+length-1;
  479.  
  480.    /* special handle 180 degrees */
  481.  
  482.    if (angle == 180)
  483.    {
  484.       /* loop through image line by line */
  485.  
  486.       for (i=line; i<=line+height-1; i++)
  487.       {
  488.          if (kbhit())    /* Allow user to quit anytime */
  489.          {
  490.             getch();
  491.             return;
  492.          }
  493.  
  494.          /* Read in each line */
  495.  
  496.          GetLine(buffer1,i,sample,length);
  497.  
  498.          /* Reverse the line */
  499.  
  500.          for (j=0; j<length; j++)
  501.             buffer2[j] = buffer1[length-j-1];
  502.  
  503.          /* Write the line out onto the screen, bottom up */
  504.  
  505.          DisplayLine(buffer2,line+height+line-i-1,sample,length);
  506.       }
  507.  
  508.    }
  509.    else /* angle != 180 */
  510.    {
  511.       /* Calculate the the sine and cosine of the angle and convert to long */
  512.       /* (The 10,000 factor will be divided out later)  */
  513.  
  514.       dangle = (((double) 3.14159 * (double) angle)/(double) 180.0);
  515.       cos_angle = (long) ((double)10000.0 * cos(dangle) + .5);
  516.       sin_angle = (long) ((double)10000.0 * sin(dangle) + .5);
  517.  
  518.       /* calculate sin and cos tables (used to speed up the code) */
  519.  
  520.       offset = (MAXDISPNS+400)/2;
  521.       for (i=0; i < MAXDISPNS+400; i++)
  522.       {
  523.          cos_table[i] = cos_angle * (long)(i-offset);
  524.          sin_table[i] = sin_angle * (long)(i-offset);
  525.       }
  526.  
  527.       /* from here on out, all calculations are done with integer arithmetic */
  528.  
  529.       /* If clipping is *not* selected, then rotate the four corners of the */
  530.       /* image, and calculate the new box size for the destination box */
  531.  
  532.       if (clip_flag == -1)
  533.       {
  534.          /* initialize corner values */
  535.  
  536.          l1 = line_start;
  537.          s1 = samp_start;
  538.          l2 = line_start;
  539.          s2 = samp_end;
  540.          l3 = line_end;
  541.          s3 = samp_start;
  542.          l4 = line_end;
  543.          s4 = samp_end;
  544.  
  545.          /* rotate upper left corner point */
  546.  
  547.          line_diff = l1 - line_center + offset;
  548.          samp_diff = s1 - samp_center + offset;
  549.          line_pixel = cos_table[line_diff] - sin_table[samp_diff];
  550.          samp_pixel = cos_table[samp_diff] + sin_table[line_diff];
  551.          line_pixel = (line_pixel + 5000L)/10000L + line_center;
  552.          samp_pixel = (samp_pixel + 5000L)/10000L + samp_center;
  553.          l1 = (int)line_pixel;
  554.          s1 = (int)samp_pixel;
  555.  
  556.          /* rotate upper right corner point */
  557.  
  558.          line_diff = l2 - line_center + offset;
  559.          samp_diff = s2 - samp_center + offset;
  560.          line_pixel = cos_table[line_diff] - sin_table[samp_diff];
  561.          samp_pixel = cos_table[samp_diff] + sin_table[line_diff];
  562.          line_pixel = (line_pixel + 5000L)/10000L + line_center;
  563.          samp_pixel = (samp_pixel + 5000L)/10000L + samp_center;
  564.          l2 = (int)line_pixel;
  565.          s2 = (int)samp_pixel;
  566.  
  567.          /* rotate lower left corner point */
  568.  
  569.          line_diff = l3 - line_center + offset;
  570.          samp_diff = s3 - samp_center + offset;
  571.          line_pixel = cos_table[line_diff] - sin_table[samp_diff];
  572.          samp_pixel = cos_table[samp_diff] + sin_table[line_diff];
  573.          line_pixel = (line_pixel + 5000L)/10000L + line_center;
  574.          samp_pixel = (samp_pixel + 5000L)/10000L + samp_center;
  575.          l3 = (int)line_pixel;
  576.          s3 = (int)samp_pixel;
  577.  
  578.          /* rotate lower right corner point */
  579.  
  580.          line_diff = l4 - line_center + offset;
  581.          samp_diff = s4 - samp_center + offset;
  582.          line_pixel = cos_table[line_diff] - sin_table[samp_diff];
  583.          samp_pixel = cos_table[samp_diff] + sin_table[line_diff];
  584.          line_pixel = (line_pixel + 5000L)/10000L + line_center;
  585.          samp_pixel = (samp_pixel + 5000L)/10000L + samp_center;
  586.          l4 = (int)line_pixel;
  587.          s4 = (int)samp_pixel;
  588.  
  589.          /* determine new sample and line values based on the four corners */
  590.          /* that we just rotated */
  591.  
  592.          line_start = l1;
  593.          if (line_start > l2) line_start = l2;
  594.          if (line_start > l3) line_start = l3;
  595.          if (line_start > l4) line_start = l4;
  596.          if (line_start < 1)  line_start = 1;
  597.          line_end = l1;
  598.          if (line_end < l2) line_end = l2;
  599.          if (line_end < l3) line_end = l3;
  600.          if (line_end < l4) line_end = l4;
  601.          if (line_end > dispnl) line_end = dispnl;
  602.          samp_start = s1;
  603.          if (samp_start > s2) samp_start = s2;
  604.          if (samp_start > s3) samp_start = s3;
  605.          if (samp_start > s4) samp_start = s4;
  606.          if (samp_start < 1)  samp_start = 1;
  607.          samp_end = s1;
  608.          if (samp_end < s2) samp_end = s2;
  609.          if (samp_end < s3) samp_end = s3;
  610.          if (samp_end < s4) samp_end = s4;
  611.          if (samp_end > dispns) samp_end = dispns;
  612.       }
  613.  
  614.       /* Loop through line-by-line of the *destination* box */
  615.  
  616.       for (i=line_start; i<=line_end; i++)
  617.       {
  618.          if (kbhit())    /* Allow user to quit anytime */
  619.          {
  620.             getch();
  621.             return;
  622.          }
  623.  
  624.          line_diff = i - line_center + offset;
  625.  
  626.          /* For each pixel in the destination box, we calculate where it */
  627.          /* resides in the refresh buffer, and display it on the screen */
  628.  
  629.          k=0;
  630.          for (j=samp_start; j<=samp_end; j++)
  631.          {
  632.             samp_diff = j - samp_center + offset;
  633.             line_pixel = sin_table[samp_diff] + cos_table[line_diff];
  634.             samp_pixel = cos_table[samp_diff] - sin_table[line_diff];
  635.             line_pixel = line_pixel/10000L + line_center;
  636.             samp_pixel = samp_pixel/10000L + samp_center;
  637.  
  638.             /* If the pixel in not in the source image box, we skip it */
  639.  
  640.             skip = FALSE;
  641.             if ((line_pixel < line)            ||
  642.                 (line_pixel > line+height-1)   ||
  643.                 (samp_pixel < sample)          ||
  644.                 (samp_pixel > sample+length-1) ||
  645.                 (samp_pixel == dispns))
  646.                skip = TRUE;
  647.  
  648.             /* If not skipped, we grab the pixel from the refresh buffer */
  649.  
  650.             if (!skip)
  651.                GetRefresh(&buffer1[k],(int)line_pixel,(int)samp_pixel,2);
  652.  
  653.             /* For skipped pixels, check if NOFILL flag is set, and if */
  654.             /* not, then set the pixel to black */
  655.  
  656.             else if (nofill_flag == -1)
  657.                buffer1[k] = 0;
  658.             else if (j < dispns)
  659.                GetRefresh(&buffer1[k],i,j,2);
  660.  
  661.             k++;
  662.          }
  663.          DisplayLine(buffer1,i,samp_start,samp_end-samp_start+1);
  664.       }
  665.  
  666.       /* Reset the coordinates of the new box to be the destination box */
  667.  
  668.       Image_Line   = line_start;
  669.       Image_Sample = samp_start;
  670.       Image_Height = line_end - line_start + 1;
  671.       Image_Length = samp_end - samp_start + 1;
  672.  
  673.    }
  674.  
  675.    /* save rotated image to the refresh buffer */
  676.  
  677.    save = RefreshLines;  /* temporarily disable refresh buffer to force the */
  678.                          /* GetLine() routine to read from the screen       */
  679.    RefreshLines = 0;
  680.    for (i=line_start; i<=line_end; i++)
  681.    {
  682.       GetLine(buffer1,i,samp_start,samp_end-samp_start+1);
  683.       PutRefresh(buffer1,i,samp_start,samp_end-samp_start+1);
  684.    }
  685.    RefreshLines = save;  /* restore */
  686. }
  687.