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