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