home *** CD-ROM | disk | FTP | other *** search
/ vsiftp.vmssoftware.com / VSIPUBLIC@vsiftp.vmssoftware.com.tar / FREEWARE / FREEWARE40.ZIP / xv310a / xvalg.c < prev    next >
C/C++ Source or Header  |  1995-06-12  |  46KB  |  1,675 lines

  1. /*
  2.  * xvalg.c - image manipulation algorithms (Blur, etc.)
  3.  *
  4.  *  Contains:
  5.  *         void AlgInit();
  6.  *         void DoAlg(int algnum);
  7.  */
  8.  
  9. #include "copyright.h"
  10.  
  11. #include "xv.h"
  12.  
  13. #ifndef M_PI
  14. #  define M_PI (3.1415926535897932385)
  15. #endif
  16.  
  17.  
  18. static void NoAlg          PARM((void));
  19. static void Blur           PARM((void));
  20. static void Sharpen        PARM((void));
  21. static void EdgeDetect     PARM((void));
  22. static void TinFoil        PARM((void));
  23. static void OilPaint       PARM((void));
  24. static void Blend          PARM((void));
  25. static void FineRotate     PARM((int));
  26. static void Pixelize       PARM((void));
  27. static void Spread         PARM((void));
  28. static void MedianFilter   PARM((void));
  29. static void saveOrigPic    PARM((void));
  30.  
  31. static void doBlurConvolv  PARM((byte *,int,int,byte *, int,int,int,int, int));
  32. static void doSharpConvolv PARM((byte *,int,int,byte *, int,int,int,int, int));
  33. static void doEdgeConvolv  PARM((byte *,int,int,byte *, int,int,int,int));
  34. static void doAngleConvolv PARM((byte *,int,int,byte *, int,int,int,int));
  35. static void doOilPaint     PARM((byte *,int,int,byte *, int,int,int,int, int));
  36. static void doBlend        PARM((byte *,int,int,byte *, int,int,int,int));
  37. static void doRotate       PARM((byte *,int,int,byte *, int,int,int,int,
  38.                  double, int));
  39. static void doPixel        PARM((byte *,int,int,byte *, int,int,int,int,
  40.                  int, int));
  41. static void doSpread       PARM((byte *,int,int,byte *, int,int,int,int, 
  42.                  int, int));
  43. static void doMedianFilter PARM((byte *,int,int,byte *, int,int,int,int, int));
  44.  
  45. static void add2bb         PARM((int *, int *, int *, int *, int, int));
  46. static void rotXfer        PARM((int, int, double *,double *,
  47.                  double,double, double));
  48.  
  49. #ifdef FOO
  50. static void intsort        PARM((int *, int));
  51. #endif
  52.  
  53. static int  start24bitAlg  PARM((byte **, byte **));
  54. static void end24bitAlg    PARM((byte *, byte *));
  55.  
  56. static void printUTime     PARM((char *));
  57.  
  58. static byte *origPic = (byte *) NULL;
  59. static int  origPicType;
  60. static byte origrmap[256], origgmap[256], origbmap[256];
  61.  
  62.  
  63. #undef TIMING_TEST
  64.  
  65. #ifdef TIMING_TEST
  66. #include <sys/time.h>
  67. #include <sys/resource.h>
  68. #endif
  69.  
  70.  
  71. /***************************/
  72. static void printUTime(str)
  73.      char *str;
  74. {
  75. #ifdef TIMING_TEST
  76.   int i;  struct rusage ru;
  77.  
  78.   i = getrusage(RUSAGE_SELF, &ru);
  79.   fprintf(stderr,"%s: utime = %d.%d seconds\n",
  80.         str, ru.ru_utime.tv_sec, ru.ru_utime.tv_usec);
  81. #endif
  82. }
  83.  
  84.  
  85.  
  86.  
  87.  
  88.  
  89. /************************************************************/
  90. void AlgInit()
  91. {
  92.   /* called whenver an image file is loaded.  disposes of origPic 
  93.      if neccessary, and points it to null */
  94.  
  95.   if (origPic) free(origPic);
  96.   origPic = (byte *) NULL;
  97.  
  98.   algMB.dim[ALG_NONE] = 1;    /* can't undo when init'ed already */
  99. }
  100.  
  101.  
  102. /************************/
  103. void DoAlg(anum)
  104.      int anum;
  105. {
  106.   /* called with a value from the algMB button.  Executes the specified
  107.      algorithm */
  108.  
  109.   switch (anum) {
  110.   case ALG_NONE:      NoAlg();        break;
  111.   case ALG_BLUR:      Blur();         break;
  112.   case ALG_SHARPEN:   Sharpen();      break;
  113.   case ALG_EDGE:      EdgeDetect();   break;
  114.   case ALG_TINF:      TinFoil();      break;
  115.   case ALG_OIL:       OilPaint();     break;
  116.   case ALG_BLEND:     Blend();        break;
  117.   case ALG_ROTATE:    FineRotate(0);  break;
  118.   case ALG_ROTATECLR: FineRotate(1);  break;
  119.   case ALG_PIXEL:     Pixelize();     break;
  120.   case ALG_SPREAD:    Spread();       break;
  121.   case ALG_MEDIAN:    MedianFilter(); break;
  122.   }
  123.  
  124.   algMB.dim[ALG_NONE] = (origPic == (byte *) NULL);
  125. }
  126.  
  127.  
  128.  
  129. /************************/
  130. static void NoAlg()
  131. {
  132.   int i;
  133.  
  134.   /* restore original picture */
  135.   if (!origPic) return;  /* none to restore */
  136.  
  137.   WaitCursor();
  138.  
  139.   KillOldPics();   /* toss the old pic/cpic/epic/theImage away */
  140.  
  141.   picType = origPicType;
  142.   Set824Menus(picType);
  143.  
  144.   pic = origPic;  origPic = NULL;
  145.  
  146.   if (picType == PIC8) {
  147.     for (i=0; i<256; i++) {
  148.       rMap[i] = origrmap[i];
  149.       gMap[i] = origgmap[i];
  150.       bMap[i] = origbmap[i];
  151.     }
  152.   }
  153.  
  154.   InstallNewPic();
  155. }
  156.  
  157.  
  158. /************************/
  159. static void Blur()
  160. {
  161.   /* runs a n*n convolution mask (all 1's) over 'pic',
  162.      producing a 24-bit version.  Then calls 24to8 to generate a new 8-bit
  163.      image, and installs it. 
  164.  
  165.      Note that 'n' must be odd for things to work properly */
  166.  
  167.   byte        *pic24, *tmpPic;
  168.   int          i, sx,sy,sw,sh, n;
  169.   static char *labels[] = { "\nOk", "\033Cancel" };
  170.   char         txt[256];
  171.   static char  buf[64] = { '3', '\0' };
  172.   
  173.   sprintf(txt, "Blur:                                   \n\n%s",
  174.       "Enter mask size (ex. 3, 5, 7, ...)");
  175.  
  176.   i = GetStrPopUp(txt, labels, 2, buf, 64, "0123456789", 1);
  177.   if (i==1 || strlen(buf)==0) return;
  178.   n = atoi(buf);
  179.  
  180.   if (n < 1 || (n&1)!=1) {
  181.     ErrPopUp("Error:  The value entered must be odd and greater than zero.", 
  182.          "\nOh!");
  183.     return;
  184.   }
  185.  
  186.   WaitCursor();
  187.  
  188.   if (HaveSelection()) GetSelRCoords(&sx,&sy,&sw,&sh);
  189.   else { sx = 0;  sy = 0;  sw = pWIDE;  sh = pHIGH; }
  190.   CropRect2Rect(&sx,&sy,&sw,&sh, 0,0,pWIDE,pHIGH);
  191.  
  192.   SetISTR(ISTR_INFO, "Blurring %s with %dx%d convolution mask...",
  193.       (HaveSelection() ? "selection" : "image"), n,n);
  194.  
  195.   if (start24bitAlg(&pic24, &tmpPic)) return;
  196.   xvbcopy((char *) pic24, (char *) tmpPic, (size_t) (pWIDE*pHIGH*3));
  197.     
  198.   doBlurConvolv(pic24, pWIDE,pHIGH, tmpPic, sx,sy,sw,sh, n);
  199.  
  200.   end24bitAlg(pic24, tmpPic);
  201. }
  202.  
  203.  
  204.  
  205. /************************/
  206. static void Sharpen()
  207. {
  208.   /* runs an edge-enhancment algorithm */
  209.  
  210.   byte        *pic24, *tmpPic;
  211.   int          i, sx,sy,sw,sh, n;
  212.   static char *labels[] = { "\nOk", "\033Cancel" };
  213.   char         txt[256];
  214.   static char  buf[64] = { '7', '5', '\0' };
  215.   
  216.   sprintf(txt, "Sharpen:                                   \n\n%s",
  217.       "Enter enhancement factor (0-99%)");
  218.  
  219.   i = GetStrPopUp(txt, labels, 2, buf, 64, "0123456789", 1);
  220.   if (i==1 || strlen(buf)==0) return;
  221.   n = atoi(buf);
  222.  
  223.   if (n>=100) {
  224.     ErrPopUp("Error:  The value entered must be less than 100%.", "\nOh!");
  225.     return;
  226.   }
  227.  
  228.   WaitCursor();
  229.  
  230.   if (HaveSelection()) GetSelRCoords(&sx,&sy,&sw,&sh);
  231.   else { sx = 0;  sy = 0;  sw = pWIDE;  sh = pHIGH; }
  232.   CropRect2Rect(&sx,&sy,&sw,&sh, 0,0,pWIDE,pHIGH);
  233.  
  234.   SetISTR(ISTR_INFO, "Sharpening %s by factor of %d%%...",
  235.       (HaveSelection() ? "selection" : "image"), n);
  236.  
  237.   if (start24bitAlg(&pic24, &tmpPic)) return;
  238.   xvbcopy((char *) pic24, (char *) tmpPic, (size_t) (pWIDE*pHIGH*3));
  239.     
  240.   doSharpConvolv(pic24, pWIDE,pHIGH, tmpPic, sx,sy,sw,sh, n);
  241.  
  242.   end24bitAlg(pic24, tmpPic);
  243. }
  244.  
  245.  
  246.  
  247. /************************/
  248. static void EdgeDetect()
  249. {
  250.   byte *pic24, *p24, *tmpPic, *tlp;
  251.   char *str;
  252.   int  i, j, v, maxv, sx,sy,sw,sh;
  253.  
  254.   WaitCursor();
  255.  
  256.   if (HaveSelection()) GetSelRCoords(&sx,&sy,&sw,&sh);
  257.   else { sx = 0;  sy = 0;  sw = pWIDE;  sh = pHIGH; }
  258.   CropRect2Rect(&sx,&sy,&sw,&sh, 0,0,pWIDE,pHIGH);
  259.  
  260.   str = "Doing edge detection...";
  261.   SetISTR(ISTR_INFO, str);
  262.  
  263.   if (start24bitAlg(&pic24, &tmpPic)) return;
  264.   xvbcopy((char *) pic24, (char *) tmpPic, (size_t) (pWIDE*pHIGH*3));
  265.  
  266.   doEdgeConvolv(pic24, pWIDE, pHIGH, tmpPic, sx,sy,sw,sh);
  267.   
  268.   SetISTR(ISTR_INFO, "%snormalizing...", str);
  269.  
  270.   /* Normalize results */
  271.   for (i=sy, maxv=0; i<sy+sh; i++) {              /* compute max value */
  272.     p24 = tmpPic + (i*pWIDE + sx) * 3;
  273.     for (j=sx; j<sx+sw; j++, p24+=3) {
  274.       v = MONO(p24[0], p24[1], p24[2]);
  275.       if (v>maxv) maxv = v;
  276.     }
  277.   }
  278.  
  279.   for (i=sy; maxv>0 && i<sy+sh; i++) {            /* normalize */
  280.     p24 = tlp = tmpPic + (i*pWIDE + sx) * 3;
  281.     for (j=0; j<sw*3; j++) {
  282.       v = (((int) *p24) * 255) / maxv;
  283.       RANGE(v,0,255);
  284.       *p24++ = (byte) v;
  285.     }
  286.   }
  287.  
  288.   end24bitAlg(pic24, tmpPic);
  289. }
  290.  
  291.  
  292. /************************/
  293. static void TinFoil()
  294. {
  295.   byte *pic24, *p24, *tmpPic, *tp, *tlp;
  296.   char *str;
  297.   int  i, j, v, maxv,sx,sy,sw,sh;
  298.   
  299.   WaitCursor();
  300.   
  301.   str = "Doing cheesy embossing effect...";
  302.   SetISTR(ISTR_INFO, str);
  303.   
  304.   if (HaveSelection()) GetSelRCoords(&sx,&sy,&sw,&sh);
  305.   else { sx = 0;  sy = 0;  sw = pWIDE;  sh = pHIGH; }
  306.   CropRect2Rect(&sx,&sy,&sw,&sh, 0,0,pWIDE,pHIGH);
  307.   
  308.   if (start24bitAlg(&pic24, &tmpPic)) return;
  309.   xvbcopy((char *) pic24, (char *) tmpPic, (size_t) (pWIDE*pHIGH*3));
  310.  
  311.   /* fill selected area of tmpPic with gray128 */
  312.   for (i=sy; i<sy+sh; i++) {
  313.     tp = tlp = tmpPic + (i*pWIDE + sx) * 3;
  314.     for (j=sx; j<sx+sw; j++) {
  315.       *tp++ = 128;  *tp++ = 128;  *tp++ = 128;
  316.     }
  317.   }
  318.   
  319.   doAngleConvolv(pic24, pWIDE, pHIGH, tmpPic, sx,sy,sw,sh);
  320.   
  321.   /* mono-ify selected area of tmpPic */
  322.   for (i=sy; i<sy+sh; i++) {
  323.     tp = tlp = tmpPic + (i*pWIDE + sx) * 3;
  324.     for (j=sx; j<sx+sw; j++, tp+=3) {
  325.       v = MONO(tp[0], tp[1], tp[2]);
  326.       RANGE(v,0,255);
  327.       tp[0] = tp[1] = tp[2] = (byte) v;
  328.     }
  329.   }
  330.     
  331.   end24bitAlg(pic24, tmpPic);
  332. }  
  333.  
  334.  
  335. /************************/
  336. static void OilPaint()
  337. {
  338.   byte *pic24, *tmpPic;
  339.   int   sx,sy,sw,sh;
  340.  
  341.   WaitCursor();
  342.  
  343.   SetISTR(ISTR_INFO, "Doing oilpaint effect...");
  344.  
  345.   if (HaveSelection()) GetSelRCoords(&sx,&sy,&sw,&sh);
  346.   else { sx = 0;  sy = 0;  sw = pWIDE;  sh = pHIGH; }
  347.   CropRect2Rect(&sx,&sy,&sw,&sh, 0,0,pWIDE,pHIGH);
  348.   
  349.   if (start24bitAlg(&pic24, &tmpPic)) return;
  350.   xvbcopy((char *) pic24, (char *) tmpPic, (size_t) (pWIDE*pHIGH*3));
  351.  
  352.   doOilPaint(pic24, pWIDE, pHIGH, tmpPic, sx,sy,sw,sh, 3);
  353.  
  354.   end24bitAlg(pic24, tmpPic);
  355. }
  356.  
  357.  
  358.  
  359. /************************/
  360. static void Blend()
  361. {
  362.   byte *pic24, *tmpPic;
  363.   int   sx,sy,sw,sh;
  364.  
  365.   if (HaveSelection()) GetSelRCoords(&sx,&sy,&sw,&sh);
  366.   else { sx = 0;  sy = 0;  sw = pWIDE;  sh = pHIGH; }
  367.   CropRect2Rect(&sx,&sy,&sw,&sh, 0,0,pWIDE,pHIGH);
  368.   
  369.   WaitCursor();
  370.  
  371.   if (start24bitAlg(&pic24, &tmpPic)) return;
  372.   xvbcopy((char *) pic24, (char *) tmpPic, (size_t) (pWIDE*pHIGH*3));
  373.  
  374.   doBlend(pic24, pWIDE, pHIGH, tmpPic, sx,sy,sw,sh);
  375.  
  376.   end24bitAlg(pic24, tmpPic);
  377. }
  378.  
  379.  
  380. /************************/
  381. static void FineRotate(clr)
  382.      int clr;
  383. {
  384.   byte        *pic24, *tmpPic;
  385.   int          i,sx,sy,sw,sh;
  386.   double       rotval;
  387.   static char *labels[] = { "\nOk", "\033Cancel" };
  388.   char         txt[256];
  389.   static char  buf[64] = { '\0' };
  390.  
  391.   sprintf(txt, "Rotate (%s):\n\nEnter rotation angle, in degrees:  (>0 = CCW)",
  392.       (clr ? "Clear" : "Copy"));
  393.  
  394.   i = GetStrPopUp(txt, labels, 2, buf, 64, "0123456789.-", 1);
  395.   if (i==1 || strlen(buf)==0) return;
  396.   rotval = atof(buf);
  397.  
  398.   if (rotval == 0.0) return;
  399.   
  400.  
  401.   if (HaveSelection()) GetSelRCoords(&sx,&sy,&sw,&sh);
  402.   else { sx = 0;  sy = 0;  sw = pWIDE;  sh = pHIGH; }
  403.   CropRect2Rect(&sx,&sy,&sw,&sh, 0,0,pWIDE,pHIGH);
  404.   
  405.   WaitCursor();
  406.  
  407.   if (start24bitAlg(&pic24, &tmpPic)) return;
  408.   xvbcopy((char *) pic24, (char *) tmpPic, (size_t) (pWIDE*pHIGH*3));
  409.  
  410.   doRotate(pic24, pWIDE, pHIGH, tmpPic, sx,sy,sw,sh, rotval, clr);
  411.  
  412.   end24bitAlg(pic24, tmpPic);
  413. }
  414.  
  415.  
  416. /************************/
  417. static void Pixelize()
  418. {
  419.   byte        *pic24, *tmpPic;
  420.   int          i,sx,sy,sw,sh, pixX,pixY,err;
  421.   static char *labels[] = { "\nOk", "\033Cancel" };
  422.   char         txt[256];
  423.   static char  buf[64] = { '4', '\0' };
  424.  
  425.   sprintf(txt, "Pixelize:\n\nEnter new pixel size, in image pixels:  %s",
  426.       "(ex. '3', '5x8')");
  427.  
  428.   i = GetStrPopUp(txt, labels, 2, buf, 64, "0123456789x", 1);
  429.   if (i==1 || strlen(buf)==0) return;
  430.  
  431.   pixX = pixY = err = 0;
  432.  
  433.   if (index(buf, 'x')) {
  434.     if (sscanf(buf, "%d x %d", &pixX, &pixY) != 2) err++;
  435.   }
  436.   else {
  437.     if (sscanf(buf, "%d", &pixX) != 1) err++;
  438.     pixY = pixX;
  439.   }
  440.  
  441.   if (pixX<1 || pixY<1 || err) {
  442.     ErrPopUp("Error:  The entered string is not valid.", "\nWho Cares!");
  443.     return;
  444.   }
  445.  
  446.   
  447.   if (HaveSelection()) GetSelRCoords(&sx,&sy,&sw,&sh);
  448.   else { sx = 0;  sy = 0;  sw = pWIDE;  sh = pHIGH; }
  449.   CropRect2Rect(&sx,&sy,&sw,&sh, 0,0,pWIDE,pHIGH);
  450.   
  451.   WaitCursor();
  452.  
  453.   if (start24bitAlg(&pic24, &tmpPic)) return;
  454.   xvbcopy((char *) pic24, (char *) tmpPic, (size_t) (pWIDE*pHIGH*3));
  455.  
  456.   doPixel(pic24, pWIDE, pHIGH, tmpPic, sx,sy,sw,sh, pixX, pixY);
  457.  
  458.   end24bitAlg(pic24, tmpPic);
  459. }
  460.  
  461.  
  462.  
  463. /************************/
  464. static void Spread()
  465. {
  466.   byte        *pic24, *tmpPic;
  467.   int          i,sx,sy,sw,sh, pixX,pixY,err;
  468.   static char *labels[] = { "\nOk", "\033Cancel" };
  469.   char         txt[256];
  470.   static char  buf[64] = { '5', '\0' };
  471.  
  472.   sprintf(txt, "Spread:\n\nEnter spread factor (or x,y factors):  %s",
  473.       "(ex. '10', '1x5')");
  474.  
  475.   i = GetStrPopUp(txt, labels, 2, buf, 64, "0123456789x", 1);
  476.   if (i==1 || strlen(buf)==0) return;
  477.  
  478.   pixX = pixY = err = 0;
  479.  
  480.   if (index(buf, 'x')) {
  481.     if (sscanf(buf, "%d x %d", &pixX, &pixY) != 2) err++;
  482.     if (pixX<0 || pixY<0) err++;
  483.   }
  484.   else {
  485.     if (sscanf(buf, "%d", &pixX) != 1) err++;
  486.     pixX = -pixX;
  487.   }
  488.  
  489.   if (!pixX && !pixY) return;     /* 0x0 has no effect */
  490.  
  491.   if (err) {
  492.     ErrPopUp("Error:  The entered string is not valid.", "\nBite Me!");
  493.     return;
  494.   }
  495.  
  496.   
  497.   if (HaveSelection()) GetSelRCoords(&sx,&sy,&sw,&sh);
  498.   else { sx = 0;  sy = 0;  sw = pWIDE;  sh = pHIGH; }
  499.   CropRect2Rect(&sx,&sy,&sw,&sh, 0,0,pWIDE,pHIGH);
  500.   
  501.   WaitCursor();
  502.  
  503.   if (start24bitAlg(&pic24, &tmpPic)) return;
  504.   xvbcopy((char *) pic24, (char *) tmpPic, (size_t) (pWIDE*pHIGH*3));
  505.  
  506.   doSpread(pic24, pWIDE, pHIGH, tmpPic, sx,sy,sw,sh, pixX, pixY);
  507.  
  508.   end24bitAlg(pic24, tmpPic);
  509. }
  510.  
  511.  
  512.  
  513. /************************/
  514. static void MedianFilter()
  515. {
  516.   /* runs median filter algorithm (for n*n rect centered around each pixel,
  517.      replace with median value */
  518.  
  519.   byte        *pic24, *tmpPic;
  520.   int          i, sx,sy,sw,sh, n;
  521.   static char *labels[] = { "\nOk", "\033Cancel" };
  522.   char         txt[256];
  523.   static char  buf[64] = { '3', '\0' };
  524.   
  525.   sprintf(txt, "DeSpeckle (median filter):                          \n\n%s",
  526.       "Enter mask size (ex. 3, 5, 7, ...)");
  527.  
  528.   i = GetStrPopUp(txt, labels, 2, buf, 64, "0123456789", 1);
  529.   if (i==1 || strlen(buf)==0) return;
  530.   n = atoi(buf);
  531.  
  532.   if (n < 1 || (n&1)!=1) {
  533.     ErrPopUp("Error:  The value entered must be odd and greater than zero.", 
  534.          "\nOh!");
  535.     return;
  536.   }
  537.  
  538.   WaitCursor();
  539.  
  540.   if (HaveSelection()) GetSelRCoords(&sx,&sy,&sw,&sh);
  541.   else { sx = 0;  sy = 0;  sw = pWIDE;  sh = pHIGH; }
  542.   CropRect2Rect(&sx,&sy,&sw,&sh, 0,0,pWIDE,pHIGH);
  543.  
  544.   SetISTR(ISTR_INFO, "DeSpeckling %s with %dx%d convolution mask...",
  545.       (HaveSelection() ? "selection" : "image"), n,n);
  546.  
  547.   if (start24bitAlg(&pic24, &tmpPic)) return;
  548.   xvbcopy((char *) pic24, (char *) tmpPic, (size_t) (pWIDE*pHIGH*3));
  549.     
  550.   doMedianFilter(pic24, pWIDE,pHIGH, tmpPic, sx,sy,sw,sh, n);
  551.  
  552.   end24bitAlg(pic24, tmpPic);
  553. }
  554.  
  555.  
  556.  
  557. /************************/
  558. static void doBlurConvolv(pic24, w, h, results, selx,sely,selw,selh, n)
  559.      byte *pic24, *results;
  560.      int   w,h, selx,sely,selw,selh, n;
  561. {
  562.  
  563.   /* convolves with an n*n array, consisting of only 1's.  
  564.      Operates on rectangular region 'selx,sely,selw,selh' (in pic coords)
  565.      Region is guaranteed to be completely within pic boundaries
  566.      'n' must be odd */
  567.  
  568.   register byte *p24;
  569.   register int   rsum,gsum,bsum;
  570.   byte          *rp;
  571.   int            i,j,k,x,y,x1,y1,count,n2;
  572.  
  573.  
  574.   printUTime("start of blurConvolv");
  575.  
  576.   n2 = n/2;
  577.  
  578.   for (y=sely; y<sely+selh; y++) {
  579.     ProgressMeter(sely, (sely+selh)-1, y, "Blur");
  580.     if ((y & 15) == 0) WaitCursor();
  581.  
  582.     p24 = pic24   + y*w*3 + selx*3;
  583.     rp  = results + y*w*3 + selx*3;
  584.  
  585.     for (x=selx; x<selx+selw; x++) {
  586.  
  587.       rsum = gsum = bsum = 0;  count = 0;
  588.  
  589.       for (y1=y-n2; y1<=y+n2; y1++) {
  590.  
  591.     if (y1>=sely && y1<sely+selh) {
  592.       p24 = pic24 + y1*w*3 +(x-n2)*3; 
  593.  
  594.       for (x1=x-n2; x1<=x+n2; x1++) {
  595.         if (x1>=selx && x1<selx+selw) {
  596.           rsum += *p24++;
  597.           gsum += *p24++;
  598.           bsum += *p24++;
  599.           count++;
  600.         }
  601.         else p24 += 3;
  602.       }
  603.     }
  604.       }
  605.  
  606.       rsum = rsum / count;
  607.       gsum = gsum / count;
  608.       bsum = bsum / count;
  609.  
  610.       RANGE(rsum,0,255);
  611.       RANGE(gsum,0,255);
  612.       RANGE(bsum,0,255);
  613.  
  614.       *rp++ = (byte) rsum;
  615.       *rp++ = (byte) gsum;
  616.       *rp++ = (byte) bsum;
  617.     }
  618.   }
  619.  
  620.  
  621.   printUTime("end of blurConvolv");
  622. }
  623.  
  624.  
  625.  
  626. /************************/
  627. static void doSharpConvolv(pic24, w, h, results, selx,sely,selw,selh, n)
  628.      byte *pic24, *results;
  629.      int   w,h, selx,sely,selw,selh, n;
  630. {
  631.   byte  *p24;
  632.   int    rv, gv, bv;
  633.   byte  *rp;
  634.   int    i,j,k,x,y,x1,y1;
  635.   double fact, ifact, hue,sat,val, vsum;
  636.   double *linem1, *line0, *linep1, *tmpptr;
  637.  
  638.   printUTime("start of sharpConvolv");
  639.  
  640.   fact  = n / 100.0;
  641.   ifact = 1.0 - fact;
  642.  
  643.   if (selw<3 || selh<3) return;  /* too small */
  644.  
  645.  
  646.   linem1 = (double *) malloc(selw * sizeof(double));
  647.   line0  = (double *) malloc(selw * sizeof(double));
  648.   linep1 = (double *) malloc(selw * sizeof(double));
  649.  
  650.   if (!linem1 || !line0 || !linep1) {
  651.     ErrPopUp("Malloc() error in doSharpConvov().", "\nDoh!");
  652.     if (linem1) free(linem1);
  653.     if (line0)  free(line0);
  654.     if (linep1) free(linep1);
  655.     return;
  656.   }
  657.  
  658.  
  659.   /* load up line arrays */
  660.   p24 = pic24 + (((sely+1)-1)*w + selx) * 3;
  661.   for (x=selx, tmpptr=line0; x<selx+selw; x++, p24+=3, tmpptr++) {
  662.     rgb2hsv((int) p24[0], (int) p24[1], (int) p24[2], &hue,&sat,&val);
  663.     *tmpptr = val;
  664.   }
  665.  
  666.   p24 = pic24 + (((sely+1)+0)*w + selx) * 3;
  667.   for (x=selx, tmpptr=linep1; x<selx+selw; x++, p24+=3, tmpptr++) {
  668.     rgb2hsv((int) p24[0], (int) p24[1], (int) p24[2], &hue,&sat,&val);
  669.     *tmpptr = val;
  670.   }
  671.  
  672.  
  673.   for (y=sely+1; y<(sely+selh)-1; y++) {
  674.     ProgressMeter(sely+1, (sely+selh)-2, y, "Sharpen");
  675.     if ((y & 15) == 0) WaitCursor();
  676.     
  677.     tmpptr = linem1;   linem1 = line0;   line0 = linep1;   linep1 = tmpptr;
  678.  
  679.     /* get next line */
  680.     p24 = pic24 + ((y+1)*w + selx) * 3;
  681.     for (x=selx, tmpptr=linep1; x<selx+selw; x++, p24+=3, tmpptr++) {
  682.       rgb2hsv((int) p24[0], (int) p24[1], (int) p24[2], &hue,&sat,&val);
  683.       *tmpptr = val;
  684.     }
  685.  
  686.     p24 = pic24   + (y*w + selx+1) * 3;
  687.     rp  = results + (y*w + selx+1) * 3;
  688.  
  689.     for (x=selx+1; x<selx+selw-1; x++, p24+=3) {
  690.       vsum = 0.0;  i = x-selx;
  691.       vsum = linem1[i-1] + linem1[i] + linem1[i+1] +
  692.          line0 [i-1] + line0 [i] + line0 [i+1] +
  693.          linep1[i-1] + linep1[i] + linep1[i+1];
  694.       
  695.       rgb2hsv((int) p24[0], (int) p24[1], (int) p24[2], &hue, &sat, &val);
  696.  
  697.       val = ((val - (fact * vsum) / 9) / ifact);
  698.       RANGE(val, 0.0, 1.0);
  699.       hsv2rgb(hue,sat,val, &rv, &gv, &bv);
  700.  
  701.       RANGE(rv,0,255);
  702.       RANGE(gv,0,255);
  703.       RANGE(bv,0,255);
  704.  
  705.       *rp++ = (byte) rv;
  706.       *rp++ = (byte) gv;
  707.       *rp++ = (byte) bv;
  708.     }
  709.   }
  710.  
  711.  
  712.   free(linem1);  free(line0);  free(linep1);
  713.  
  714.   printUTime("end of sharpConvolv");
  715. }
  716.  
  717.  
  718.  
  719. /************************/
  720. static void doEdgeConvolv(pic24, w, h, results, selx,sely,selw,selh)
  721.      byte *pic24, *results;
  722.      int   w, h, selx,sely,selw,selh;
  723. {
  724.  
  725.   /* convolves with two edge detection masks (vertical and horizontal)
  726.      simultaneously, taking Max(abs(results)) 
  727.      
  728.      The two masks are (hard coded):
  729.  
  730.           -1 0 1             -1 -1 -1
  731.       H = -1 0 1     and V =  0  0  0
  732.           -1 0 1              1  1  1
  733.  
  734.      divided into 
  735.            -1 0 0         0 0 0         0 0 1        0  1 0
  736.        a =  0 0 0 ,  b = -1 0 1 ,  c =  0 0 0 ,  d = 0  0 0 .
  737.             0 0 1         0 0 0        -1 0 0        0 -1 0
  738.  
  739.      So H = a + b + c,  V = a - c - d.
  740.      gradient = max(abs(H),abs(V)).
  741.           
  742.      Also, only does pixels in which the masks fit fully onto the picture
  743.      (no pesky boundary conditionals)  */
  744.  
  745.  
  746.   register byte *p24;
  747.   register int   bperlin, a, b, c, d, rsum, gsum, bsum;
  748.   byte          *rp;
  749.   int            i, x, y;
  750.  
  751.  
  752.   printUTime("start of edgeConvolv");
  753.  
  754.   bperlin = w * 3;
  755.  
  756.   for (y=sely+1; y<sely+selh-1; y++) {
  757.     ProgressMeter(sely+1, (sely+selh-1)-1, y, "Edge Detect");
  758.     if ((y & 63) == 0) WaitCursor();
  759.  
  760.     rp  = results + (y*w + selx+1)*3;
  761.     p24 = pic24   + (y*w + selx+1)*3;
  762.  
  763.     for (x=selx+1; x<selx+selw-1; x++, p24+=3) {
  764.       /* RED PLANE */
  765.       a = p24[bperlin+3]  - p24[-bperlin-3]; /* bottom right - top left */
  766.       b = p24[3]   -        p24[-3];         /* mid    right - mid left */
  767.       c = p24[-bperlin+3] - p24[bperlin-3];  /* top    right - bottom left */
  768.       d = p24[-bperlin]   - p24[bperlin];    /* top    mid   - bottom mid */
  769.  
  770.       rsum = a + b + c;      /* horizontal gradient */
  771.       if (rsum < 0) rsum = -rsum;
  772.       a = a - c - d;         /* vertical gradient */
  773.       if (a < 0) a = -a;
  774.       if (a > rsum) rsum = a;
  775.       rsum /= 3;
  776.  
  777.       /* GREEN PLANE */
  778.       a = p24[bperlin+4]  - p24[-bperlin-2]; /* bottom right - top left */
  779.       b = p24[4]   -        p24[-2];         /* mid    right - mid left */
  780.       c = p24[-bperlin+4] - p24[bperlin-2];  /* top    right - bottom left */
  781.       d = p24[-bperlin+1] - p24[bperlin+1];  /* top    mid   - bottom mid */
  782.  
  783.       gsum = a + b + c;      /* horizontal gradient */
  784.       if (gsum < 0) gsum = -gsum;
  785.       a = a - c - d;         /* vertical gradient */
  786.       if (a < 0) a = -a;
  787.       if (a > gsum) gsum = a;
  788.       gsum /= 3;
  789.  
  790.       /* BLUE PLANE */
  791.       a = p24[bperlin+5]  - p24[-bperlin-1]; /* bottom right - top left */
  792.       b = p24[5]   -        p24[-1];         /* mid    right - mid left */
  793.       c = p24[-bperlin+5] - p24[bperlin-1];  /* top    right - bottom left */
  794.       d = p24[-bperlin+2] - p24[bperlin+2];  /* top    mid   - bottom mid */
  795.  
  796.       bsum = a + b + c;      /* horizontal gradient */
  797.       if (bsum < 0) bsum = -bsum;
  798.       a = a - c - d;         /* vertical gradient */
  799.       if (a < 0) a = -a;
  800.       if (a > bsum) bsum = a;
  801.       bsum /= 3;
  802.  
  803.       *rp++ = (byte) rsum;
  804.       *rp++ = (byte) gsum;
  805.       *rp++ = (byte) bsum;
  806.     }
  807.   }
  808.  
  809.   printUTime("end of edgeConvolv");
  810. }
  811.  
  812.  
  813.  
  814. /************************/
  815. static void doAngleConvolv(pic24, w, h, results, selx,sely,selw,selh)
  816.      byte *pic24, *results;
  817.      int   w, h, selx,sely,selw,selh;
  818. {
  819.  
  820.   /* convolves with edge detection mask, at 45 degrees to horizontal.
  821.      
  822.      The mask is (hard coded):
  823.  
  824.              -2 -1 0
  825.              -1  0 1
  826.               0  1 2
  827.           
  828.      Also, only does pixels in which the masks fit fully onto the picture
  829.      (no pesky boundary conditionals)
  830.  
  831.      Adds value of rsum,gsum,bsum to results pic */
  832.  
  833.   register byte *p24;
  834.   register int   bperlin,rsum,gsum,bsum;
  835.   byte          *rp;
  836.   int            i, x,y;
  837.  
  838.  
  839.   printUTime("start of doAngleConvolv");
  840.  
  841.   bperlin = w * 3;
  842.  
  843.   for (y=sely+1; y<sely+selh-1; y++) {
  844.     ProgressMeter(sely+1, (sely+selh-1)-1, y, "Convolve");
  845.     if ((y & 63) == 0) WaitCursor();
  846.  
  847.     rp  = results + (y*w + selx+1)*3;
  848.     p24 = pic24   + (y*w + selx+1)*3;
  849.  
  850.     for (x=selx+1; x<selx+selw-1; x++, p24+=3) {
  851.  
  852.       /* compute weighted average for *p24 (pic24[x,y]) */
  853.       rsum = gsum = bsum = 0;
  854.  
  855.       rsum -= (p24[-bperlin-3] * 2);   /* top left */
  856.       gsum -= (p24[-bperlin-2] * 2);
  857.       bsum -= (p24[-bperlin-1] * 2);
  858.  
  859.       rsum -= p24[-bperlin];           /* top mid */
  860.       gsum -= p24[-bperlin+1];
  861.       bsum -= p24[-bperlin+2];
  862.  
  863.       rsum -= p24[-3];                 /* mid left */
  864.       gsum -= p24[-2];
  865.       bsum -= p24[-1];
  866.  
  867.       rsum += p24[3];                  /* mid right */
  868.       gsum += p24[4];
  869.       bsum += p24[5];
  870.  
  871.       rsum += p24[bperlin];            /* bottom mid */
  872.       gsum += p24[bperlin+1];
  873.       bsum += p24[bperlin+2];
  874.  
  875.       rsum += (p24[bperlin+3] * 2);    /* bottom right */
  876.       gsum += (p24[bperlin+4] * 2);
  877.       bsum += (p24[bperlin+5] * 2);
  878.  
  879.       rsum = rsum / 8;
  880.       gsum = gsum / 8;
  881.       bsum = bsum / 8;
  882.  
  883.       rsum += rp[0];  RANGE(rsum,0,255);
  884.       gsum += rp[1];  RANGE(gsum,0,255);
  885.       bsum += rp[2];  RANGE(bsum,0,255);
  886.  
  887.       *rp++ = (byte) rsum;
  888.       *rp++ = (byte) gsum;
  889.       *rp++ = (byte) bsum;
  890.     }
  891.   }
  892.  
  893.   printUTime("end of edgeConvolv");
  894. }
  895.  
  896.  
  897.  
  898.  
  899. /************************/
  900. static void doOilPaint(pic24, w, h, results, selx,sely,selw,selh, n)
  901.      byte *pic24, *results;
  902.      int   w, h, selx,sely,selw,selh, n;
  903. {
  904.  
  905.   /* does an 'oil transfer', as described in the book "Beyond Photography",
  906.      by Holzmann, chapter 4, photo 7.  It is a sort of localized smearing.
  907.  
  908.      The following algorithm (but no actual code) was borrowed from
  909.      'pgmoil.c', written by Wilson Bent (whb@hoh-2.att.com),
  910.      and distributed as part of the PBMPLUS package.
  911.  
  912.      for each pixel in the image (assume, for a second, a grayscale image),
  913.      compute a histogram of the n*n rectangle centered on the pixel.
  914.      replace the pixel with the color that had the greatest # of hits in
  915.      the histogram.  Note that 'n' should be odd. 
  916.  
  917.      I've modified the algorithm to do the *right* thing for RGB images.
  918.      (jhb, 6/94)  */
  919.  
  920.  
  921.   register byte *pp;
  922.   register int   bperlin, rsum,gsum,bsum;
  923.   byte          *rp, *p24, *plin;
  924.   int            i,j,k,x,y,n2,col,cnt,maxcnt;
  925.   int           *nnrect;
  926.  
  927.   printUTime("start of doOilPaint");
  928.  
  929.   if (n & 1) n++;   /* n must be odd */
  930.  
  931.   bperlin = w * 3;
  932.   n2 = n/2;
  933.  
  934.   /* nnrect[] is an n*n array of ints, with '-1' meaning 'outside the region'
  935.      otherwise they'll have 24-bit RGB values */
  936.   
  937.   nnrect = (int *) malloc(n * n * sizeof(int));
  938.   if (!nnrect) FatalError("can't malloc nnrect[] in doOilPaint()\n");
  939.  
  940.   for (y=sely; y<sely+selh; y++) {
  941.     if ((y & 15) == 0) WaitCursor();
  942.     ProgressMeter(sely, (sely+selh)-1, y, "Oil Paint");
  943.  
  944.     p24 = pic24 + ((y-n2)*w + selx-n2)*3;   /* pts to top-left of mask */
  945.     rp  = results + (y*w + selx)*3;
  946.     
  947.     for (x=selx; x<selx+selw; x++) {
  948.       /* fill 'nnrect' with valid pixels from n*n region centered round x,y */
  949.       pp = plin = p24;
  950.       for (i=y-n2, k=0; i<y+n2; i++) {
  951.     for (j=x-n2; j<x+n2; j++, k++, pp+=3) {
  952.       if (i>=sely && i<sely+selh && j>=selx && j<selx+selw) { 
  953.         nnrect[k] = (((int) pp[0])<<16) | (((int) pp[1])<<8) | pp[2];
  954.       }
  955.       else nnrect[k] = -1;
  956.     }
  957.     plin += bperlin;  pp = plin;
  958.       }
  959.  
  960.       
  961.       /* find 'most popular color' in nnrect, not counting '-1' */
  962.       maxcnt = cnt = col = 0;
  963.       for (i=0; i<n*n; i++) {
  964.     if (nnrect[i] != -1) {
  965.       cnt = 1;
  966.       for (j=i+1; j<n*n; j++) { /* count it */
  967.         if (nnrect[i] == nnrect[j]) cnt++;
  968.       }
  969.       if (cnt>maxcnt) { col = nnrect[i];  maxcnt = cnt; }
  970.     }
  971.       }
  972.  
  973.       *rp++ = (byte) ((col>>16) & 0xff);
  974.       *rp++ = (byte) ((col>>8)  & 0xff);
  975.       *rp++ = (byte) ((col)     & 0xff);
  976.  
  977.       p24 += 3;
  978.     }
  979.   }
  980.  
  981.   free(nnrect);
  982.   printUTime("end of doOilPaint");
  983. }
  984.  
  985.  
  986. /************************/
  987. static void doBlend(pic24, w, h, results, selx,sely,selw,selh)
  988.      byte *pic24, *results;
  989.      int   w, h, selx,sely,selw,selh;
  990. {
  991.   /* 'blends' a rectangular region out of existence.  computes the average
  992.      color of all the pixels on the edge of the rect, stores this in the
  993.      center, and for each pixel in the rect, replaces it with a weighted
  994.      average of the center color, and the color on the edge that intersects
  995.      a line drawn from the center to the point */
  996.  
  997.   byte  *p24;
  998.   int    i,x,y;
  999.   int    cx,cy,cR,cG,cB;
  1000.   int    ex,ey,eR,eG,eB;
  1001.   int    dx,dy,r,g,b;
  1002.   double rf,gf,bf, slope,dslope, d,d1, ratio;
  1003.  
  1004.   if (selw<3 || selh<3) return;        /* too small to blend */
  1005.  
  1006.   printUTime("start of blend");
  1007.  
  1008.   /*** COMPUTE COLOR OF CENTER POINT ***/
  1009.  
  1010.   i = 0;  rf = gf = bf = 0.0;
  1011.   for (x=selx; x<selx+selw; x++) {
  1012.     p24 = pic24 + (sely*w + x) * 3;
  1013.     rf += (double) p24[0];  gf += (double) p24[1];  bf += (double) p24[2];
  1014.     i++;
  1015.  
  1016.     p24 = pic24 + ((sely+selh-1)*w + x) * 3;
  1017.     rf += (double) p24[0];  gf += (double) p24[1];  bf += (double) p24[2];
  1018.     i++;
  1019.   }
  1020.   for (y=sely; y<sely+selh; y++) {
  1021.     p24 = pic24 + (y*w + selx) * 3;
  1022.     rf += (double) p24[0];  gf += (double) p24[1];  bf += (double) p24[2];
  1023.     i++;
  1024.     
  1025.     p24 = pic24 + (y*w + (selx+selw-1)) * 3;
  1026.     rf += (double) p24[0];  gf += (double) p24[1];  bf += (double) p24[2];
  1027.     i++;
  1028.   }
  1029.  
  1030.   cR = (int) (rf / i);
  1031.   cG = (int) (gf / i);
  1032.   cB = (int) (bf / i);
  1033.  
  1034.   cx = selx + selw/2;  cy = sely + selh/2;
  1035.  
  1036.   dslope = ((double) selh) / selw;
  1037.  
  1038.   /* for each point on INTERIOR of region, do the thing */
  1039.   for (y=sely+1; y<sely+selh-1; y++) {
  1040.     if ((y & 15) == 0) WaitCursor();
  1041.     ProgressMeter(sely+1, (sely+selh-1)-1, y, "Blend");
  1042.  
  1043.     for (x=selx+1; x<selx+selw-1; x++) {
  1044.  
  1045.       /* compute edge intercept point ex,ey */
  1046.       dx = x - cx;  dy = y - cy;
  1047.       if (dx==0 && dy==0) { ex = selx;  ey = sely; }  /* don't care */
  1048.       else if (dx==0) {    ex = cx;  ey = (dy<0) ? sely : sely+selh-1; }
  1049.       else if (dy==0) {    ey = cy;  ex = (dx<0) ? selx : selx+selw-1; }
  1050.       else { 
  1051.     slope = ((double) dy) / dx;
  1052.     if (fabs(slope) > fabs(dslope)) {   /* y axis is major */
  1053.       ey = (dy<0) ? sely : sely+selh-1;
  1054.       ex = cx + ((ey-cy) * dx) / dy;
  1055.     }
  1056.     else {                              /* x axis is major */
  1057.       ex = (dx<0) ? selx : selx+selw-1;
  1058.       ey = cy + ((ex-cx) * dy) / dx;
  1059.     }
  1060.       }
  1061.  
  1062.       /* let's play it safe... */
  1063.       RANGE(ex, selx, selx+selw-1);
  1064.       RANGE(ey, sely, sely+selh-1);
  1065.  
  1066.       /* given (cx,cy), (x,y), and (ex,ey), compute d, d1 */
  1067.       d  = sqrt((double) (dx * dx) + (double) (dy * dy));
  1068.       d1 = sqrt((double) ((ex-cx) * (ex-cx)) + (double) ((ey-cy) * (ey-cy)));
  1069.       ratio = pow(d/d1, 2.0);
  1070.  
  1071.       /* fetch color of ex,ey */
  1072.       p24 = pic24 + (ey*w + ex) * 3;
  1073.       eR = p24[0];  eG=p24[1];  eB=p24[2];
  1074.  
  1075.       /* compute new color for x,y */
  1076.       if (dx==0 && dy==0) { r=cR;  g=cG;  b=cB; }
  1077.       else {
  1078.     r = cR + (int) ((eR-cR) * ratio);
  1079.     g = cG + (int) ((eG-cG) * ratio);
  1080.     b = cB + (int) ((eB-cB) * ratio);
  1081.     RANGE(r,0,255);
  1082.     RANGE(g,0,255);
  1083.     RANGE(b,0,255);
  1084.       }
  1085.  
  1086.       /* and stuff it... */
  1087.       p24 = results + (y*w + x) * 3;
  1088.       p24[0] = (byte) r;  p24[1] = (byte) g;  p24[2] = (byte) b;
  1089.     }
  1090.   }
  1091.  
  1092.   printUTime("end of blend");
  1093. }
  1094.  
  1095.   
  1096.  
  1097. /************************/
  1098. static void doRotate(pic24, w, h, results, selx,sely,selw,selh, rotval, clear)
  1099.      byte  *pic24, *results;
  1100.      int    w, h, selx,sely,selw,selh,clear;
  1101.      double rotval;
  1102. {
  1103.   /* rotates a rectangular region (selx,sely,selw,selh) of an image (pic24,w,h)
  1104.      by the amount specified in degrees (rotval), and stores the result in
  1105.      'results', which is also a w*h 24-bit image.  The rotated bits are
  1106.      clipped to fit in 'results'.  If 'clear', the (unrotated) rectangular
  1107.      region is cleared (in results) first.  
  1108.      sel[x,y,w,h] is guaranteed to be within image bounds */
  1109.  
  1110.   byte  *pp, *dp;
  1111.   int    i, j, x, y, ox,oy, ox1,oy1;
  1112.   int    rx1,ry1, rx2,ry2, rx3,ry3, rx4,ry4;
  1113.   int    rbx, rby, rbx1, rby1, rbw, rbh;
  1114.   double rotrad, xf,yf, xfrac,yfrac, px,py, apx, apy, cfx,cfy;
  1115.  
  1116.   if (selw<1 || selh<1) return;
  1117.  
  1118.   printUTime("start of rotate");
  1119.  
  1120.   /*
  1121.    * cfx,cfy  -  center point of sel rectangle (double) 
  1122.    * rx1,ry1  -  top-left  of sel, rotated
  1123.    * rx2,ry2  -  bot-left  of sel, rotated
  1124.    * rx3,ry3  -  top-right of sel, rotated
  1125.    * rx4,ry4  -  bot-right of sel, rotated
  1126.    * rbx, rby, rbw, rbh  -  bounding box for rotated sel, clipped to pic
  1127.    */
  1128.  
  1129.   rotrad = rotval * M_PI / 180.0;
  1130.  
  1131.   /* compute corner points of 'sel' after rotation */
  1132.   cfx = selx + (selw-1)/2.0;  cfy = sely + (selh-1)/2.0;
  1133.  
  1134.   rotXfer(selx,        sely,      &xf, &yf, cfx, cfy, rotrad);
  1135.   rx1 = (int) (xf + ((xf<0) ? -0.5 : 0.5));
  1136.   ry1 = (int) (yf + ((yf<0) ? -0.5 : 0.5));
  1137.  
  1138.   rotXfer(selx,        sely+selh, &xf, &yf, cfx, cfy, rotrad);
  1139.   rx2 = (int) (xf + ((xf<0) ? -0.5 : 0.5));
  1140.   ry2 = (int) (yf + ((yf<0) ? -0.5 : 0.5));
  1141.  
  1142.   rotXfer(selx+selw,   sely,      &xf, &yf, cfx, cfy, rotrad);
  1143.   rx3 = (int) (xf + ((xf<0) ? -0.5 : 0.5));
  1144.   ry3 = (int) (yf + ((yf<0) ? -0.5 : 0.5));
  1145.  
  1146.   rotXfer(selx+selw,   sely+selh, &xf, &yf, cfx, cfy, rotrad);
  1147.   rx4 = (int) (xf + ((xf<0) ? -0.5 : 0.5));
  1148.   ry4 = (int) (yf + ((yf<0) ? -0.5 : 0.5));
  1149.  
  1150.   /* compute bounding box for rotated rect */
  1151.  
  1152.   rbx = rbx1 = rx1;  rby = rby1 = ry1;
  1153.   add2bb(&rbx, &rby, &rbx1, &rby1, rx2,ry2);
  1154.   add2bb(&rbx, &rby, &rbx1, &rby1, rx3,ry3);
  1155.   add2bb(&rbx, &rby, &rbx1, &rby1, rx4,ry4);
  1156.   rbw = rbx1 - rbx;  rbh = rby1 - rby;
  1157.  
  1158.   /* make it a *little* bigger, just to be safe... */
  1159.   rbx--;  rby--;  rbw+=2;  rbh+=2;
  1160.   CropRect2Rect(&rbx, &rby, &rbw, &rbh, 0,0,w,h);
  1161.  
  1162.   if (clear) {           /* clear original selection in results pic */
  1163.     for (i=sely; i<sely+selh; i++) {
  1164.       dp = results + (i*w + selx)*3;
  1165.       for (j=selx; j<selx+selw; j++) {
  1166.     *dp++ = clearR;
  1167.     *dp++ = clearG;
  1168.     *dp++ = clearB;
  1169.       }
  1170.     }
  1171.   }
  1172.  
  1173.  
  1174.   /* now, for each pixel in rb[x,y,w,h], do the inverse rotation to see if
  1175.      it would be in the original unrotated selection rectangle.  if it *is*,
  1176.      compute and store an appropriate color, otherwise skip it */
  1177.  
  1178.   for (y=rby; y<rby+rbh; y++) {
  1179.     dp = results + (y * w + rbx) * 3;
  1180.  
  1181.     if ((y & 15) == 0) WaitCursor();
  1182.     ProgressMeter(rby, rby+rbh-1, y, "Rotate");
  1183.  
  1184.     for (x=rbx; x<rbx+rbw; x++, dp+=3) {
  1185.       rotXfer(x,y, &xf, &yf, cfx, cfy, -rotrad);
  1186.  
  1187.       /* cheat a little... */
  1188.       if (xf < 0.0  &&  xf > -0.5) xf = 0.0;
  1189.       if (yf < 0.0  &&  yf > -0.5) yf = 0.0;
  1190.  
  1191.       ox = (int) floor(xf);   oy = (int) floor(yf);
  1192.  
  1193.       if (PTINRECT(ox,oy, selx,sely,selw,selh)) {
  1194.     int    p0r,p0g,p0b, p1r,p1g,p1b, p2r,p2g,p2b, p3r,p3g,p3b;
  1195.     int    rv,gv,bv;
  1196.     double rd,gd,bd, p0wgt, p1wgt, p2wgt, p3wgt;
  1197.     
  1198.     /* compute the color, same idea as in Smooth**().  The color
  1199.        will be a linear combination of the colors of the center pixel,
  1200.        its left-or-right neighbor, its top-or-bottom neighbor, and
  1201.        its corner neighbor.  *which* neighbors are used are determined by
  1202.        the position of the fractional part of xf,yf within the 1-unit
  1203.        square of the pixel.  */
  1204.  
  1205.     /* compute px,py: fractional offset from center of pixel (x.5,y.5) */
  1206.     xfrac = xf - ox;        /* 0 - .9999 */
  1207.     yfrac = yf - oy;
  1208.     px = ((xfrac >= .5) ? (xfrac - .5) : (-.5 + xfrac));
  1209.     py = ((yfrac >= .5) ? (yfrac - .5) : (-.5 + yfrac));
  1210.     apx = fabs(px);  apy = fabs(py);
  1211.  
  1212.     /* get neighbor colors:  p0col, p1col, p2col, p3col */
  1213.     ox1 = ox + ((px < 0.0) ? -1 : 1);
  1214.     oy1 = oy + ((py < 0.0) ? -1 : 1);
  1215.  
  1216.     pp = pic24 + (oy * w + ox) * 3;
  1217.     p0r = pp[0];  p0g = pp[1];  p0b = pp[2];                   /* ctr */
  1218.  
  1219.     if (ox1 >= selx && ox1 < selx+selw) {
  1220.       pp = pic24 + (oy * w + ox1) * 3;
  1221.       p1r = pp[0];  p1g = pp[1];  p1b = pp[2];                 /* l/r */
  1222.       p1wgt = apx * (1.0 - apy);
  1223.     }
  1224.     else { p1r=p1g=p1b=0;  p1wgt = 0.0; }
  1225.  
  1226.     if (oy1 >= sely && oy1 < sely+selh) {
  1227.       pp = pic24 + (oy1 * w + ox) * 3;
  1228.       p2r = pp[0];  p2g = pp[1];  p2b = pp[2];                 /* t/b */
  1229.       p2wgt = apy * (1.0 - apx);
  1230.     }
  1231.     else { p2r=p2g=p2b=0;  p2wgt = 0.0; }
  1232.  
  1233.     if (ox1>=selx && ox1<selx+selw && oy1>=sely && oy1<sely+selh){
  1234.       pp = pic24 + (oy1 * w + ox1) * 3;
  1235.       p3r = pp[0];  p3g = pp[1];  p3b = pp[2];                 /* diag */
  1236.       p3wgt = apx * apy;
  1237.     }
  1238.     else { p3r=p3g=p3b=0;  p3wgt = 0.0; }
  1239.  
  1240.     p1wgt = p1wgt * .7;        /* black art */
  1241.     p2wgt = p2wgt * .7;
  1242.     p3wgt = p3wgt * .7;
  1243.  
  1244.     p0wgt = 1.0 - (p1wgt + p2wgt + p3wgt);
  1245.  
  1246.     /* okay, compute and store resulting color */
  1247.     rd = p0r * p0wgt + p1r * p1wgt + p2r * p2wgt + p3r * p3wgt;
  1248.     gd = p0g * p0wgt + p1g * p1wgt + p2g * p2wgt + p3g * p3wgt;
  1249.     bd = p0b * p0wgt + p1b * p1wgt + p2b * p2wgt + p3b * p3wgt;
  1250.  
  1251.     rv = (int) (rd + 0.5);
  1252.     gv = (int) (gd + 0.5);
  1253.     bv = (int) (bd + 0.5);
  1254.  
  1255.     RANGE(rv,0,255);  RANGE(gv,0,255);  RANGE(bv,0,255);
  1256.  
  1257. #ifdef ROTATE_FOO
  1258. if (0)    {
  1259.   fprintf(stderr,"--------\n%3d,%3d in results maps to %6.2f,%6.2f in pic24\n",
  1260.       x,y,xf,yf);
  1261.   fprintf(stderr,"ox,oy = %3d,%3d (%3d,%3d)  frac=%4.2f,%4.2f  px,py=%4.2f,%4.2f\n", ox,oy, ox1,oy1, xfrac,yfrac, px,py);
  1262.   fprintf(stderr,"Colors: 0:%02x%02x%02x 1:%02x%02x%02x 2:%02x%02x%02x 3:%02x%02x%02x\n", p0r,p0g,p0b, p1r,p1g,p1b, p2r,p2g,p2b, p3r,p3g,p3b);
  1263.   fprintf(stderr,"Weights: 0[%f]  1[%f]  2[%f]  3[%f] -> %3d,%3d,%3d\n",
  1264.       p0wgt, p1wgt, p2wgt, p3wgt, rv, gv, bv);
  1265. }
  1266. #endif /* ROTATE_FOO */
  1267.  
  1268.     dp[0] = (byte) (rv&0xff);  
  1269.     dp[1] = (byte) (gv&0xff);  
  1270.     dp[2] = (byte) (bv&0xff);  
  1271.       }
  1272.     }
  1273.   }
  1274.   printUTime("end of rotate");
  1275. }
  1276.  
  1277.  
  1278. /***********************************************/
  1279. static void add2bb(x1, y1, x2, y2, x,y)
  1280.      int *x1, *y1, *x2, *y2, x,y;
  1281. {
  1282.   if (x < *x1) *x1 = x;
  1283.   if (x > *x2) *x2 = x;
  1284.   if (y < *y1) *y1 = y;
  1285.   if (y > *y2) *y2 = y;
  1286. }
  1287.  
  1288.  
  1289. /***********************************************/
  1290. static void rotXfer(x,y, rx,ry, cx,cy, rad)
  1291.      int x,y;
  1292.      double *rx, *ry, cx, cy, rad;
  1293. {
  1294.   /* take point x,y, rotate it 'rad' radians around cx,cy, return rx,ry */
  1295.   double d, xf, yf, ang;
  1296.  
  1297.   xf = x;  yf = y;
  1298.  
  1299.   d  = sqrt((xf-cx) * (xf-cx) + (yf-cy) * (yf-cy));
  1300.   if      ((xf-cx) != 0.0) {
  1301.     ang = atan((cy-yf) / (xf-cx));                 /* y-axis flip */
  1302.     if ((xf-cx) < 0) ang += M_PI;
  1303.   }
  1304.  
  1305.   else if ((yf-cy) > 0.0) ang = M_PI * 3.0 / 2.0;
  1306.   else                    ang = M_PI * 1.0 / 2.0;
  1307.  
  1308.   *rx = (double) cx + (d * cos(ang + rad));
  1309.   *ry = (double) cy - (d * sin(ang + rad));
  1310.  
  1311. #ifdef FOO
  1312.   fprintf(stderr,"rotXfer:  rotating (%4d,%4d) %7.2f degrees around",
  1313.       x,y, rad*180.0 / M_PI);
  1314.   fprintf(stderr,"(%4d,%4d) -> %7.2f %7.2f  (d=%f ang=%f)\n", 
  1315.       cx,cy, *rx,*ry, d, ang);
  1316. #endif
  1317. }
  1318.   
  1319.  
  1320.  
  1321. /************************/
  1322. static void doPixel(pic24, w, h, results, selx,sely,selw,selh, pixX, pixY)
  1323.      byte *pic24, *results;
  1324.      int   w, h, selx,sely,selw,selh, pixX,pixY;
  1325. {
  1326.   /* runs 'pixelization' algorithm.  replaces each pixX-by-pixY region 
  1327.      (smaller on edges) with the average color within that region */
  1328.   
  1329.   byte  *pp;
  1330.   int    nwide, nhigh, i,j, x,y, x1,y1, stx,sty;
  1331.   int    nsum, rsum, gsum, bsum;
  1332.   
  1333.   printUTime("start of pixelize");
  1334.   
  1335.   /* center grid on selection */
  1336.   nwide = (selw + pixX-1) / pixX;
  1337.   nhigh = (selh + pixY-1) / pixY;
  1338.   
  1339.   stx = selx - (nwide*pixX - selw)/2;
  1340.   sty = sely - (nhigh*pixY - selh)/2;
  1341.   
  1342.   y = sty;
  1343.   for (i=0; i<nhigh; i++, y+=pixY) {
  1344.     ProgressMeter(0, nhigh-1, i, "Pixelize");
  1345.     
  1346.     x = stx;
  1347.     for (j=0; j<nwide; j++, x+=pixX) {
  1348.       
  1349.       /* COMPUTE AVERAGE COLOR FOR RECT:[x,y,pixX,pixY] */
  1350.       nsum = rsum = gsum = bsum = 0;
  1351.       for (y1=y; y1<y+pixY; y1++) {
  1352.     pp = pic24 + (y1 * w + x) * 3;
  1353.     for (x1=x; x1<x+pixX; x1++) {
  1354.       if (PTINRECT(x1,y1, selx,sely,selw,selh)) {
  1355.         nsum++;
  1356.         rsum += *pp++;  gsum += *pp++;  bsum += *pp++;
  1357.       }
  1358.     }
  1359.       }
  1360.       
  1361.       if (nsum>0) {   /* just to be safe... */
  1362.     rsum /= nsum;  gsum /= nsum;  bsum /= nsum;
  1363.     RANGE(rsum,0,255);  RANGE(gsum,0,255);  RANGE(bsum,0,255);
  1364.       }
  1365.       
  1366.       
  1367.       /* STORE color in rect:[x,y,pixX,pixY] */
  1368.       for (y1=y; y1<y+pixY; y1++) {
  1369.     if (!j && (y1 & 255)==0) WaitCursor();
  1370.     
  1371.     pp = results + (y1 * w + x) * 3;
  1372.     for (x1=x; x1<x+pixX; x1++, pp+=3) {
  1373.       if (PTINRECT(x1,y1, selx,sely,selw,selh)) {
  1374.         pp[0] = (byte) rsum;  pp[1] = (byte) gsum;  pp[2] = (byte) bsum;
  1375.       }
  1376.     }
  1377.       }
  1378.     }
  1379.   }
  1380.  
  1381.   printUTime("end of pixelize");
  1382. }
  1383.  
  1384.   
  1385.  
  1386. /************************/
  1387. static void doSpread(pic24, w, h, results, selx,sely,selw,selh, pixX, pixY)
  1388.      byte *pic24, *results;
  1389.      int   w, h, selx,sely,selw,selh, pixX,pixY;
  1390. {
  1391.   /* runs 'spread' algorithm.  For each pixel in the image, swaps it with
  1392.      a random pixel near it.  Distances of random pixels are controlled
  1393.      by pixX,pixY.  If pixX<0, it is treated as a single 'distance' value
  1394.      (after being abs()'d). */
  1395.  
  1396.   /* assumes that initially 'results' is a copy of pic24.  Doesn't 
  1397.      even look at pic24 */
  1398.   
  1399.   byte  *pp, *dp, r,g,b;
  1400.   int    x,y, dx,dy, x1,y1, d, xrng, xoff, yrng, yoff, i,j;
  1401.   int    minx, maxx, miny, maxy, rdist;
  1402.   time_t nowT;
  1403.  
  1404.   time(&nowT);
  1405.   srandom((unsigned int) nowT);
  1406.   
  1407.   printUTime("start of spread");
  1408.  
  1409.   for (y=sely; y<sely+selh; y++) {
  1410.     ProgressMeter(sely, sely+selh-1, y, "Spread");
  1411.     pp = results + (y * w + selx) * 3;
  1412.     for (x=selx; x<selx+selw; x++, pp+=3) {
  1413.  
  1414.       if (pixX < 0) {
  1415.     /* compute a random neighbor within radius 'd', cropped to 'sel' */
  1416.  
  1417.     d = abs(pixX);
  1418.  
  1419.     minx = x - d;    if (minx < selx)       minx = selx;
  1420.     maxx = x + d;    if (maxx >= selx+selw) maxx = selx+selw-1;
  1421.     x1 = minx + abs(random()) % ((maxx - minx) + 1);
  1422.  
  1423.     miny = y - d;    if (miny < sely)       miny = sely;
  1424.     maxy = y + d;    if (maxy >= sely+selh) maxy = sely+selh-1;
  1425.  
  1426.     rdist = d - abs(x1 - x);
  1427.     if (y - miny > rdist) miny = (y-rdist);
  1428.     if (maxy - y > rdist) maxy = (y+rdist);
  1429.  
  1430.     y1 = miny + abs(random()) % ((maxy - miny) + 1);
  1431.       }
  1432.  
  1433.       else {
  1434.     /* compute a neighbor within +/-pixX by +/-pixY, cropped to sel */
  1435.  
  1436.     minx = x - pixX;  if (minx < selx)       minx = selx;
  1437.     maxx = x + pixX;  if (maxx >= selx+selw) maxx = selx+selw-1;
  1438.     x1 = minx + abs(random()) % ((maxx - minx) + 1);
  1439.  
  1440.     miny = y - pixY;  if (miny < sely)       miny = sely;
  1441.     maxy = y + pixY;  if (maxy >= sely+selh) maxy = sely+selh-1;
  1442.     y1 = miny + abs(random()) % ((maxy - miny) + 1);
  1443.       }
  1444.  
  1445.       if (PTINRECT(x1,y1, selx,sely,selw,selh)) {  /* should always be true */
  1446.     dp = results + (y1 * w + x1) * 3;
  1447.     r     = pp[0];  g     = pp[1];  b     = pp[2];
  1448.     pp[0] = dp[0];  pp[1] = dp[1];  pp[2] = dp[2];
  1449.     dp[0] = r;      dp[1] = g;      dp[2] = b;
  1450.       }
  1451.     }
  1452.   }
  1453.   printUTime("end of spread");
  1454. }
  1455.  
  1456.   
  1457.  
  1458. /************************/
  1459. static void doMedianFilter(pic24, w, h, results, selx,sely,selw,selh, n)
  1460.      byte *pic24, *results;
  1461.      int   w,h, selx,sely,selw,selh, n;
  1462. {
  1463.   /* runs the median filter algorithm
  1464.      Operates on rectangular region 'selx,sely,selw,selh' (in pic coords)
  1465.      Region is guaranteed to be completely within pic boundaries
  1466.      'n' must be odd */
  1467.  
  1468.   register byte *p24;
  1469.   register int   rsum,gsum,bsum;
  1470.   byte          *rp;
  1471.   int            i,j,k,x,y,x1,y1,count,n2,nsq,c2;
  1472.   int           *rtab, *gtab, *btab;
  1473.  
  1474.   printUTime("start of doMedianFilter");
  1475.  
  1476.   n2 = n/2;  nsq = n * n;
  1477.  
  1478.   rtab = (int *) malloc(nsq * sizeof(int));
  1479.   gtab = (int *) malloc(nsq * sizeof(int));
  1480.   btab = (int *) malloc(nsq * sizeof(int));
  1481.   if (!rtab || !gtab || !btab) FatalError("can't malloc in doMedianFilter!");
  1482.  
  1483.   for (y=sely; y<sely+selh; y++) {
  1484.     ProgressMeter(sely, (sely+selh)-1, y, "DeSpeckle");
  1485.     if ((y & 15) == 0) WaitCursor();
  1486.  
  1487.     p24 = pic24   + y*w*3 + selx*3;
  1488.     rp  = results + y*w*3 + selx*3;
  1489.  
  1490.     for (x=selx; x<selx+selw; x++) {
  1491.  
  1492.       rsum = gsum = bsum = 0;  count = 0;
  1493.  
  1494.       for (y1=y-n2; y1<=y+n2; y1++) {
  1495.  
  1496.     if (y1>=sely && y1<sely+selh) {
  1497.       p24 = pic24 + y1*w*3 +(x-n2)*3; 
  1498.  
  1499.       for (x1=x-n2; x1<=x+n2; x1++) {
  1500.         if (x1>=selx && x1<selx+selw) {
  1501.           rtab[count] = *p24++;
  1502.           gtab[count] = *p24++;
  1503.           btab[count] = *p24++;
  1504.           count++;
  1505.         }
  1506.         else p24 += 3;
  1507.       }
  1508.     }
  1509.       }
  1510.  
  1511.  
  1512.       /* now sort the rtab,gtab,btab arrays, (using shell sort) 
  1513.      and pick the middle value.  doing it in-line, rather than 
  1514.      as a function call (ie, 'qsort()') , for speed */
  1515.       {  
  1516.     int i,j,t,d;
  1517.     
  1518.     for (d=count/2;  d>0;  d=d/2) {
  1519.       for (i=d; i<count; i++) {
  1520.         for (j=i-d;  j>=0 && rtab[j]>rtab[j+d];  j-=d) {
  1521.           t = rtab[j];  rtab[j] = rtab[j+d];  rtab[j+d] = t;
  1522.         }
  1523.  
  1524.         for (j=i-d;  j>=0 && gtab[j]>gtab[j+d];  j-=d) {
  1525.           t = gtab[j];  gtab[j] = gtab[j+d];  gtab[j+d] = t;
  1526.         }
  1527.  
  1528.         for (j=i-d;  j>=0 && btab[j]>btab[j+d];  j-=d) {
  1529.           t = btab[j];  btab[j] = btab[j+d];  btab[j+d] = t;
  1530.         }
  1531.       }
  1532.     }
  1533.       }
  1534.       
  1535.       c2 = count/2;
  1536.       *rp++ = (byte) ( (count&1) ? rtab[c2] : (rtab[c2] + rtab[c2-1])/2);
  1537.       *rp++ = (byte) ( (count&1) ? gtab[c2] : (gtab[c2] + gtab[c2-1])/2);
  1538.       *rp++ = (byte) ( (count&1) ? btab[c2] : (btab[c2] + btab[c2-1])/2);
  1539.     }
  1540.   }
  1541.   
  1542.   free(rtab);  free(gtab);  free(btab);
  1543.   printUTime("end of doMedianFilter");
  1544. }
  1545.  
  1546.  
  1547. #ifdef FOO
  1548. /***********************************************/
  1549. static void intsort(a, n)
  1550.      int *a, n;
  1551. {
  1552.   /* uses the shell-sort algorithm.  for the relatively small data sets 
  1553.      we'll be using, should be quicker than qsort() because of all the
  1554.      function calling overhead associated with qsort(). */
  1555.  
  1556.   int i,j,t,d;
  1557.  
  1558.   for (d=n/2;  d>0;  d=d/2) {
  1559.     for (i=d; i<n; i++) {
  1560.       for (j=i-d;  j>=0 && a[j]>a[j+d];  j-=d) {
  1561.     t = a[j];  a[j] = a[j+d];  a[j+d] = t;
  1562.       }
  1563.     }
  1564.   }
  1565. }
  1566. #endif /* FOO */
  1567.  
  1568.  
  1569. /***********************************************/
  1570. static int start24bitAlg(pic24, tmpPic)
  1571.      byte **pic24, **tmpPic;
  1572. {
  1573.   /* generates a 24-bit version of 'pic', if neccessary, and also mallocs
  1574.    * a pWIDE*pHIGH*3 24-bit output pic.  
  1575.    *
  1576.    * Returns '1' if there's some sort of screwup, '0' if cool
  1577.    */
  1578.  
  1579.  
  1580.   if (picType == PIC8) {
  1581.     *pic24 = Conv8to24(pic, pWIDE, pHIGH, rMap, gMap, bMap);
  1582.     if (!*pic24) { SetCursors(-1);  return 1; }
  1583.   }
  1584.   else *pic24 = pic;
  1585.  
  1586.  
  1587.   /* need to create another w*h*3 pic to hold results */
  1588.   *tmpPic = (byte *) calloc((size_t) (pWIDE * pHIGH * 3), (size_t) 1);
  1589.   if (!(*tmpPic)) {
  1590.     SetCursors(-1);
  1591.     ErrPopUp("Unable to malloc() tmp 24-bit image in start24bitAlg()", 
  1592.          "\nTough!");
  1593.     if (picType == PIC8) free(*pic24);
  1594.     return 1;
  1595.   }
  1596.  
  1597.   return 0;
  1598. }
  1599.  
  1600.  
  1601.  
  1602. /***********************************************/
  1603. static void end24bitAlg(pic24, outPic)
  1604.      byte *pic24, *outPic;
  1605. {
  1606.   /* given pic24, and outPic, which has the new 24-bit image, installs it */
  1607.  
  1608.  
  1609.   saveOrigPic();  /* also kills pic/cpic/epic/egampic/theImage, NOT pic24 */
  1610.  
  1611.   /* copy results to pic24 */
  1612.   xvbcopy((char *) outPic, (char *) pic24, (size_t) (pWIDE*pHIGH*3)); 
  1613.   free(outPic);
  1614.  
  1615.   if (picType == PIC8) {
  1616.     pic = Conv24to8(pic24, pWIDE, pHIGH, ncols, rMap,gMap,bMap);
  1617.     free(pic24);
  1618.     if (!pic) { 
  1619.       SetCursors(-1);
  1620.       ErrPopUp("Some sort of failure occured in 24to8 conversion\n","\nDamn!");
  1621.       NoAlg(); 
  1622.       return;
  1623.     }
  1624.   }
  1625.   else pic = pic24;
  1626.  
  1627.   InstallNewPic();
  1628. }
  1629.  
  1630.  
  1631. /************************/
  1632. static void saveOrigPic()
  1633. {
  1634.   /* saves original picture into origPic, if it hasn't already been done.
  1635.      This allows us to undo algorithms...  
  1636.  
  1637.      Also, frees all pics, (except 'pic', if we're in PIC24 mode) */
  1638.  
  1639.  
  1640.   int i;
  1641.  
  1642.   FreeEpic();
  1643.   if (cpic && cpic != pic) free(cpic);
  1644.   xvDestroyImage(theImage);
  1645.   theImage = NULL;
  1646.   cpic = NULL;
  1647.  
  1648.   if (!origPic) {
  1649.     /* make a backup copy of 'pic' */
  1650.     origPic = (byte *) malloc((size_t)(pWIDE*pHIGH*((picType==PIC8) ? 1 : 3)));
  1651.     if (!origPic) FatalError("out of memory in 'saveOrigPic()'");
  1652.     xvbcopy((char *) pic, (char *) origPic, 
  1653.         (size_t) (pWIDE * pHIGH * ((picType==PIC8) ? 1 : 3)));
  1654.  
  1655.     origPicType = picType;
  1656.  
  1657.     if (picType == PIC8) {
  1658.       for (i=0; i<256; i++) {   /* save old colormap */
  1659.     origrmap[i] = rorg[i];
  1660.     origgmap[i] = gorg[i];
  1661.     origbmap[i] = borg[i];
  1662.       }
  1663.     }
  1664.   }
  1665.  
  1666.  
  1667.   if (picType != PIC24) {  /* kill pic, as well */
  1668.     if (pic) free(pic);
  1669.     pic = NULL;
  1670.   }
  1671. }
  1672.  
  1673.  
  1674.  
  1675.