1 /* +-------------------------------------------------------------------+ */
2 /* | Copyright 1993, David Koblas (koblas@netcom.com)		       | */
3 /* | Copyright 1995, 1996 Torsten Martinsen (bullestock@dk-online.dk)  | */
4 /* |								       | */
5 /* | Permission to use, copy, modify, and to distribute this software  | */
6 /* | and its documentation for any purpose is hereby granted without   | */
7 /* | fee, provided that the above copyright notice appear in all       | */
8 /* | copies and that both that copyright notice and this permission    | */
9 /* | notice appear in supporting documentation.	 There is no	       | */
10 /* | representations about the suitability of this software for	       | */
11 /* | any purpose.  this software is provided "as is" without express   | */
12 /* | or implied warranty.					       | */
13 /* |								       | */
14 /* +-------------------------------------------------------------------+ */
15 
16 /* $Id: iprocess.c,v 1.18 2005/03/20 20:15:32 demailly Exp $ */
17 
18 #include <unistd.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <math.h>
22 #include <dlfcn.h>
23 
24 #include <X11/Intrinsic.h>
25 #include <X11/Shell.h>
26 #include <X11/StringDefs.h>
27 #include <X11/Xatom.h>
28 
29 #include "xaw_incdir/AsciiText.h"
30 #include "xaw_incdir/Command.h"
31 #include "xaw_incdir/Form.h"
32 #include "xaw_incdir/Paned.h"
33 
34 #include "Paint.h"
35 #include "PaintP.h"
36 #include "xpaint.h"
37 #include "image.h"
38 #include "menu.h"
39 #include "messages.h"
40 #include "misc.h"
41 #include "protocol.h"
42 #include "graphic.h"
43 
44 #ifdef XAW3D
45 #define BORDERWIDTH 1
46 #else
47 #define BORDERWIDTH 0
48 #endif
49 
50 typedef struct {
51     Widget shell, pane, name, program, cclog;
52     int mode;
53     char *scriptfile;
54 } LocalInfo;
55 
56 typedef struct {
57     unsigned char color[3];
58     int ratio;
59 } BaseTransform;
60 
61 #define BUFSIZE 65536
62 
63 void *dl_filter = NULL;
64 void *dl_proc = NULL;
65 void *dl_image = NULL;
66 
67 /*
68 **  Some real image processing
69  */
70 
71 extern int file_isSpecialImage;
72 extern int file_transparent;
73 
74 extern imageprocessinfo ImgProcessInfo;
75 extern FILE * openTempFile(char **np);
76 extern void removeTempFile(void);
77 
78 #define CLAMP(low, value, high) \
79 		if (value < low) value = low; else if (value > high) value = high
80 #define DEG2RAD	(M_PI/180.0)
81 
82 #define ConvMatrixSize	3
83 typedef double ConvMatrix[ConvMatrixSize * ConvMatrixSize];
84 
85 static int *histogram(Image * input);
86 static LocalInfo *head;
87 
88 static PaintMenuItem fileMenu[] =
89 {
90 #define FILE_LOAD 0
91     MI_SIMPLE("load"),
92 #define FILE_SAVE 1
93     MI_SIMPLE("save"),
94 #define FILE_SAVEAS 2
95     MI_SIMPLE("saveas"),
96 #define FILE_EDIT 3
97     MI_SIMPLE("editor"),
98 #define FILE_CLOSE 4
99     MI_SIMPLE("close"),
100 };
101 static PaintMenuItem predefMenu[] =
102 {
103 #define PREDEF_FILTERS 0
104     MI_SIMPLE("filters"),
105 #define PREDEF_IMAGES 1
106     MI_SIMPLE("images"),
107 #define PREDEF_3D_CURVES 2
108     MI_SIMPLE("3d_curves"),
109 #define PREDEF_3D_SURFACES 3
110     MI_SIMPLE("3d_surfaces"),
111 #define PREDEF_LAYERS 4
112     MI_SIMPLE("layers"),
113 #define PREDEF_PROCEDURES 5
114     MI_SIMPLE("procedures"),
115 #define PREDEF_BATCH 6
116     MI_SIMPLE("batch"),
117 #define PREDEF_SEPARATOR 7
118     MI_SEPARATOR(),
119 #define PREDEF_HELP 8
120     MI_SIMPLECB("help", HelpDialog, "script_editor"),
121 };
122 
123 static PaintMenuBar printMenuBar[] =
124 {
125     {None, "script_files", XtNumber(fileMenu), fileMenu},
126     {None, "script_predef", XtNumber(predefMenu), predefMenu},
127 };
128 
129 static Image *
convolve(Image * input,double * mat,int n,BaseTransform * base,Boolean absFlag)130 convolve(Image * input, double *mat, int n,
131 	 BaseTransform *base, Boolean absFlag)
132 {
133     int x, y, xx, yy, xv, yv;
134     double sum;
135     unsigned char *p;
136     unsigned char *op;
137     double r, g, b;
138     int ir, ig, ib;
139     Image *output;
140 
141     const int xy = -n/2;
142     const int ye = input->height + xy;
143     const int xe = input->width + xy;
144     const int hh = input->height + input->height;
145     const int ww = input->width + input->width;
146 
147     output = ImageNew(input->width, input->height);
148     op = output->data;
149 
150     sum = 0;
151     for (yy = 0; yy < n; yy++) {
152 	for (xx = 0; xx < n; xx++) {
153 	    sum += mat[xx + n * yy];
154 	}
155     }
156 
157     if (sum <= 0) sum = 1;
158 
159     for (y = xy; y < ye; y++) {
160 	for (x = xy; x < xe; x++) {
161 	    r = g = b = 0;
162 	    for (yy = 0; yy < n; yy++) {
163 		for (xx = 0; xx < n; xx++) {
164 		    xv = x + xx;
165 		    yv = y + yy;
166 		    if (xv < 0)
167 			xv = -xv;
168 		    if (yv < 0)
169 			yv = -yv;
170 		    if (xv >= input->width) {
171 			xv = xv % ww;
172 			if (xv >= input->width)
173 			    xv = ww - xv - 1;
174 		    }
175 		    if (yv >= input->height) {
176 			yv = yv % hh;
177 			if (yv >= input->height)
178 			    yv = hh - yv - 1;
179 		    }
180 		    p = ImagePixel(input, xv, yv);
181 		    r += (double) *p++ * mat[xx + n * yy];
182 		    g += (double) *p++ * mat[xx + n * yy];
183 		    b += (double) *p * mat[xx + n * yy];
184 		}
185 	    }
186 	    if (absFlag) {
187 		if (r < 0)
188 		    r = -r;
189 		if (g < 0)
190 		    g = -g;
191 		if (b < 0)
192 		    b = -b;
193 	    }
194 	    ir = r / sum;
195 	    ig = g / sum;
196 	    ib = b / sum;
197 	    if (base) {
198 	        /* linear rescaling */
199                 double f = (double) base->ratio / 100.0;
200 	        ir = f * ir + (int)base->color[0] + 0.5;
201 	        ig = f * ig + (int)base->color[1] + 0.5;
202 	        ib = f * ib + (int)base->color[2] + 0.5;
203 	    }
204 	    CLAMP(0, ir, 255);
205 	    CLAMP(0, ig, 255);
206 	    CLAMP(0, ib, 255);
207 
208 	    *op++ = ir;
209 	    *op++ = ig;
210 	    *op++ = ib;
211 	}
212 
213 	if (y % 16 == 0)
214 	    StateTimeStep();
215     }
216 
217     return output;
218 }
219 
220 /*
221 **  rescale values from 0..255
222  */
223 static void
normalize(Image * image)224 normalize(Image * image)
225 {
226     int i, count;
227     unsigned char *sp;
228     unsigned char *ip;
229     unsigned int maxval = 0;
230 
231     if (image->cmapSize != 0) {
232 	sp = image->cmapData;
233 	count = image->cmapSize * 3;
234     } else {
235 	sp = image->data;
236 	count = image->width * image->height * 3;
237     }
238 
239     for (ip = sp, i = 0; i < count; i++, ip++)
240 	if (*ip > maxval)
241 	    maxval = *ip;
242     if (maxval == 0)
243 	return;
244     for (ip = sp, i = 0; i < count; i++, ip++)
245         *ip = (((unsigned int)(*ip)) * 255) / maxval;
246 }
247 
248 /*
249 **  Convert image into a monochrome image
250  */
251 
252 unsigned char
GreyScale(unsigned char r,unsigned char g,unsigned char b)253 GreyScale(unsigned char r, unsigned char g, unsigned char b)
254 {
255     return (30*r + 59*g + 11*b)/100;
256 }
257 
258 static void
monochrome(Image * image)259 monochrome(Image * image)
260 {
261     int i, count;
262     unsigned char *sp;
263     unsigned char *ip;
264     int v;
265 
266     if (image->cmapSize != 0) {
267 	sp = image->cmapData;
268 	count = image->cmapSize;
269     } else {
270 	sp = image->data;
271 	count = image->width * image->height;
272     }
273 
274     for (ip = sp, i = 0; i < count; i++) {
275         v = GreyScale(ip[0], ip[1], ip[2]);
276 	*ip++ = v;
277 	*ip++ = v;
278 	*ip++ = v;
279     }
280 }
281 
282 
283 Image *
ImageSmooth(Image * input)284 ImageSmooth(Image * input)
285 {
286     double *mat;
287     int n, i;
288     Image *output;
289 
290     /*
291      * Build n x n convolution matrix (all 1's)
292      */
293     n = ImgProcessInfo.smoothMaskSize;
294     mat = xmalloc(n * n * sizeof(double));
295     for (i = 0; i < n * n; ++i)
296 	mat[i] = 1.0;
297 
298     output = convolve(input, mat, n, NULL, False);
299     free(mat);
300     return output;
301 }
302 
303 Image *
ImageSharpen(Image * input)304 ImageSharpen(Image * input)
305 {
306     static ConvMatrix mat =
307     {
308 	-1, -2, -1,
309 	-2, 20, -2,
310 	-1, -2, -1
311     };
312     return convolve(input, mat, ConvMatrixSize, NULL, False);
313 }
314 
315 Image *
ImageMerge(Image * input)316 ImageMerge(Image * input)
317 {
318     Image *output, *canvas;
319     int width = input->width, height = input->height;
320     int x, y, x1, x2, y1, y2, alpha = 0;
321     unsigned char *ip, *cp, *op, *ia, *ca, *oa;
322     unsigned int p=0, q=0;
323     unsigned long omi=0;
324     double d, coef=0;
325 
326     canvas = ImgProcessInfo.canvas;
327 
328     if (ImgProcessInfo.mergeType == 0) {
329         d = (double)ImgProcessInfo.mergePercent / 10001.0;
330         d = 160000.0 * d/(1.0-d);
331         coef = (double) ((width-1) * (height-1));
332         coef = d/(coef * coef);
333     }
334     if (ImgProcessInfo.mergeType == 1) {
335         p = ImgProcessInfo.mergePercent;
336         q = 10000 - q;
337     }
338 
339     output = ImageNew(width, height);
340     ImageCopyData(input, output);
341     if (input->alpha && canvas->alpha) {
342         alpha = width*height;
343         output->alpha = (unsigned char*) xmalloc(alpha);
344         memcpy(output->alpha, input->alpha, alpha);
345         omi = (unsigned long)output->alpha - (unsigned long)input->alpha;
346     }
347 
348     x1 = MAX(0, -ImgProcessInfo.shiftX);
349     x2 = MIN(input->width, canvas->width-ImgProcessInfo.shiftX);
350     y1 = MAX(0, -ImgProcessInfo.shiftY);
351     y2 = MIN(input->height, canvas->height-ImgProcessInfo.shiftY);
352 
353     for (y = y1; y < y2; y++) {
354         for (x = x1; x < x2; x++) {
355 	    ip = ImagePixel(input, x, y);
356 	    op = ImagePixel(output, x, y);
357 	    cp = ImagePixel(canvas, x+ImgProcessInfo.shiftX, y+ImgProcessInfo.shiftY);
358             if (ImgProcessInfo.mergeType <= 1) {
359 	        if (ImgProcessInfo.mergeType == 0) {
360 		    p =   (int) ( (double) (x * (width-1-x)) *
361 			          (double) (y * (height-1-y)) * coef );
362                     if (p > 10000) p = 10000;
363 		    q = 10000 - p;
364 		}
365                 op[0] = (p * ip[0] + q * cp[0])/10000;
366                 op[1] = (p * ip[1] + q * cp[1])/10000;
367                 op[2] = (p * ip[2] + q * cp[2])/10000;
368 	    } else {
369 	        if (ImgProcessInfo.mergeType == 2) {
370 		    op[0] = MAX(ip[0], cp[0]);
371 		    op[1] = MAX(ip[1], cp[1]);
372 		    op[2] = MAX(ip[2], cp[2]);
373 	        } else {
374 		    op[0] = MIN(ip[0], cp[0]);
375 		    op[1] = MIN(ip[1], cp[1]);
376 		    op[2] = MIN(ip[2], cp[2]);
377 		}
378 	    }
379             if (alpha) {
380 	        ia = input->alpha + x + y * input->width;
381 	        oa = input->alpha + omi;
382                 ca = canvas->alpha + x + ImgProcessInfo.shiftX +
383 		     (y+ImgProcessInfo.shiftY) * canvas->width;
384                 if (ImgProcessInfo.mergeType <= 1)
385                     *oa = (p * *ia + q * *ca)/10000;
386                 else
387                 if (ImgProcessInfo.mergeType == 2)
388 		    *oa = MAX(*ia, *ca);
389                 else
390 		    *oa = MIN(*ia, *ca);
391 	    }
392         }
393     }
394 
395     return output;
396 }
397 
398 Image *
ImageEdge(Image * input)399 ImageEdge(Image * input)
400 {
401     static ConvMatrix mat =
402     {
403 	-1, -2,  0,
404 	-2,  0,  2,
405 	 0,  2,  1
406     };
407     Image *image = convolve(input, mat, ConvMatrixSize, NULL, True);
408 
409     normalize(image);
410 
411     return image;
412 }
413 
414 Image *
ImageEmboss(Image * input)415 ImageEmboss(Image * input)
416 {
417     static ConvMatrix mat =
418     {
419 	-1, -2,  0,
420 	-2,  0,  2,
421 	 0,  2,  1
422     };
423     static BaseTransform base;
424 
425     Image *image;
426 
427     memset(base.color, ImgProcessInfo.embossBG, 3);
428     base.ratio = ImgProcessInfo.embossRatio;
429     image = convolve(input, mat, ConvMatrixSize, &base, False);
430 
431     monochrome(image);
432 
433     return image;
434 }
435 
436 Image *
ImageInvert(Image * input)437 ImageInvert(Image * input)
438 {
439     Image *output;
440     unsigned char *op;
441 
442 #define MEMORY_SAVINGS 1
443 #if MEMORY_SAVINGS
444     unsigned char *ip;
445     int i, count;
446 
447     /*
448     **	 If the input has a colormap, just invert that.
449      */
450     if (input->cmapSize != 0) {
451 	output = ImageNewCmap(input->width, input->height, input->cmapSize);
452 	ip = input->cmapData;
453 	op = output->cmapData;
454         output->isGrey = input->isGrey;
455         output->isBW = input->isBW;
456         count = input->cmapSize;
457 
458 	memcpy(output->data, input->data,
459 	     sizeof(char) * input->scale * input->width * input->height);
460 
461     } else {
462 	output = ImageNew(input->width, input->height);
463 	ip = input->data;
464 	op = output->data;
465 	count = input->width * input->height;
466     }
467 
468     for (i = 0; i < count; i++) {
469          *op++ = 255 - *ip++;
470 	 *op++ = 255 - *ip++;
471 	 *op++ = 255 - *ip++;
472     }
473 
474 #else
475     int x, y;
476     unsigned char *p;
477 
478     output = ImageNew(input->width, input->height);
479     op = output->data;
480 
481     for (y = 0; y < input->height; y++)
482     {
483 	for (x = 0; x < input->width; x++)
484 	{
485 	    p = ImagePixel(input, x, y);
486 	    *op++ = 255 - *p++;
487 	    *op++ = 255 - *p++;
488 	    *op++ = 255 - *p;
489 	}
490     }
491 #endif
492 
493     return output;
494 }
495 
496 Image *
ImageOilPaint(Image * input)497 ImageOilPaint(Image * input)
498 {
499     Image *output = ImageNew(input->width, input->height);
500     unsigned char *op = output->data;
501     int x, y, xx, yy, i;
502     int rVal, gVal, bVal;
503     int rCnt, gCnt, bCnt;
504     int rHist[256], gHist[256], bHist[256];
505     int oilArea_2;
506 
507 
508     oilArea_2 = ImgProcessInfo.oilArea / 2;
509 
510     for (y = 0; y < input->height; y++) {
511 	for (x = 0; x < input->width; x++) {
512 	    /*
513 	    **	compute histogram of (on-screen hunk of) n*n
514 	    **	  region centered plane
515 	     */
516 
517 	    rCnt = gCnt = bCnt = 0;
518 	    rVal = gVal = bVal = 0;
519 	    for (i = 0; i < XtNumber(rHist); i++)
520 		rHist[i] = gHist[i] = bHist[i] = 0;
521 
522 	    for (yy = y - oilArea_2; yy < y + oilArea_2; yy++) {
523 		if (yy < 0 || yy >= input->height)
524 		    continue;
525 		for (xx = x - oilArea_2; xx < x + oilArea_2; xx++) {
526 		    int c, p;
527 		    unsigned char *rgb;
528 
529 		    if (xx < 0 || xx >= input->width)
530 			continue;
531 
532 		    rgb = ImagePixel(input, xx, yy);
533 
534 		    if ((c = ++rHist[(p = rgb[0]) / 4]) > rCnt) {
535 			rVal = p;
536 			rCnt = c;
537 		    }
538 		    if ((c = ++gHist[(p = rgb[1]) / 4]) > gCnt) {
539 			gVal = p;
540 			gCnt = c;
541 		    }
542 		    if ((c = ++bHist[(p = rgb[2]) / 4]) > bCnt) {
543 			bVal = p;
544 			bCnt = c;
545 		    }
546 		}
547 	    }
548 
549 	    *op++ = rVal;
550 	    *op++ = gVal;
551 	    *op++ = bVal;
552 	}
553 
554 	if (y % 16 == 0)
555 	    StateTimeStep();
556     }
557 
558     return output;
559 }
560 
561 
562 /*
563  * Add random noise to an image.
564  */
565 Image *
ImageAddNoise(Image * input)566 ImageAddNoise(Image * input)
567 {
568     unsigned char *p;
569     int x, y, r, g, b, ddelta;
570     Image *output = ImageNew(input->width, input->height);
571     unsigned char *op = output->data;
572 
573 
574     ddelta = ImgProcessInfo.noiseDelta * 2;
575 
576     for (y = 0; y < input->height; y++) {
577 	for (x = 0; x < input->width; x++) {
578 	    p = ImagePixel(input, x, y);
579 	    r = p[0] + ddelta * gauss();
580 	    CLAMP(0, r, 255);
581 	    g = p[1] + ddelta * gauss();
582 	    CLAMP(0, g, 255);
583 	    b = p[2] + ddelta * gauss();
584 	    CLAMP(0, b, 255);
585 	    *op++ = r;
586 	    *op++ = g;
587 	    *op++ = b;
588 	}
589     }
590     return output;
591 }
592 
593 /*
594  * This function works in-place.
595  * Because it swaps pixels in the input image, which may be color mapped
596  * or not, is has knowledge about the Image format that should probably
597  * have stayed in image.h. Too bad.
598  */
599 Image *
ImageSpread(Image * input)600 ImageSpread(Image * input)
601 {
602     int w = input->width, h = input->height;
603     unsigned char *p, *np;
604     int x, y, minx, miny, maxx, maxy, xn, yn, dist;
605 
606     dist = ImgProcessInfo.spreadDistance;
607     for (y = 0; y < h; y++) {
608 	for (x = 0; x < w; x++) {
609 	    p = ImagePixel(input, x, y);
610 	    /* find random neighbour pixel within +- dist */
611 	    minx = x - dist;
612 	    if (minx < 0)
613 		minx = 0;
614 	    maxx = x + dist;
615 	    if (maxx >= w)
616 		maxx = w - 1;
617 	    xn = RANDOMI2(minx, maxx);
618 
619 	    miny = y - dist;
620 	    if (miny < 0)
621 		miny = 0;
622 	    maxy = y + dist;
623 	    if (maxy >= h)
624 		maxy = h - 1;
625 	    yn = RANDOMI2(miny, maxy);
626 	    /* swap pixel with neighbour */
627 	    np = ImagePixel(input, xn, yn);
628 	    if (input->cmapSize == 0) {
629 		unsigned char rn, gn, bn;
630 
631 		rn = np[0];
632 		gn = np[1];
633 		bn = np[2];
634 		np[0] = p[0];
635 		np[1] = p[1];
636 		np[2] = p[2];
637 		p[0] = rn;
638 		p[1] = gn;
639 		p[2] = bn;
640 	    } else if (input->cmapSize > 256) {
641 		unsigned short i, *ps, *nps;
642 
643 		ps = &((unsigned short *) input->data)[y * w + x];
644 		nps = &((unsigned short *) input->data)[yn * w + xn];
645 		i = *nps;
646 		*nps = *ps;
647 		*ps = i;
648 	    } else {		/* colour map of 256 or less */
649 		unsigned char i;
650 
651 		p = &input->data[y * w + x];
652 		np = &input->data[yn * w + xn];
653 		i = *np;
654 		*np = *p;
655 		*p = i;
656 	    }
657 	}
658     }
659 
660     return input;
661 }
662 
663 Image *
ImageBlend(Image * input)664 ImageBlend(Image * input)
665 {
666     int w = input->width, h = input->height;
667     Image *output = ImageNew(w, h);
668     unsigned char *op = output->data;
669     unsigned char *rgb;
670     int x, y, n, ox, oy, px, py, dx, dy;
671     int rSum, gSum, bSum, r, g, b;
672     double diagonal, gradient, ap;
673 
674 
675     ox = w / 2;
676     oy = h / 2;
677     diagonal = ((double) h) / w;
678 
679     /*
680      * Compute average of pixels on edge of region. While we are
681      * at it, we copy the edge to the output as well.
682      */
683     rSum = gSum = bSum = 0;
684     for (x = 0; x < w; ++x) {
685 	rgb = ImagePixel(input, x, 0);
686 	r = rgb[0];
687 	g = rgb[1];
688 	b = rgb[2];
689 	rSum += r;
690 	gSum += g;
691 	bSum += b;
692 	*op++ = r;
693 	*op++ = g;
694 	*op++ = b;
695     }
696     op = ImagePixel(output, 0, h - 1);
697     for (x = 0; x < w; ++x) {
698 	rgb = ImagePixel(input, x, h - 1);
699 	r = rgb[0];
700 	g = rgb[1];
701 	b = rgb[2];
702 	rSum += r;
703 	gSum += g;
704 	bSum += b;
705 	*op++ = r;
706 	*op++ = g;
707 	*op++ = b;
708     }
709     for (y = 1; y < h - 1; ++y) {
710 	rgb = ImagePixel(input, 0, y);
711 	r = rgb[0];
712 	g = rgb[1];
713 	b = rgb[2];
714 	rSum += r;
715 	gSum += g;
716 	bSum += b;
717 	op = ImagePixel(output, 0, y);
718 	*op++ = r;
719 	*op++ = g;
720 	*op++ = b;
721 
722 	rgb = ImagePixel(input, w - 1, y);
723 	r = rgb[0];
724 	g = rgb[1];
725 	b = rgb[2];
726 	rSum += r;
727 	gSum += g;
728 	bSum += b;
729 	op = ImagePixel(output, w - 1, y);
730 	*op++ = r;
731 	*op++ = g;
732 	*op++ = b;
733     }
734 
735     n = 2 * (w + h - 2);	/* total # of pixels on edge */
736     rSum /= n;
737     gSum /= n;
738     bSum /= n;
739     op = ImagePixel(output, 1, 1);
740 
741     /* compute colour of each point in the interior */
742     for (y = 1; y < h - 1; y++) {
743 	for (x = 1; x < w - 1; x++) {
744 	    dx = x - ox;
745 	    dy = y - oy;
746 	    if (dx == 0 && dy == 0) {	/* this is the centre point */
747 		r = rSum;
748 		g = gSum;
749 		b = bSum;
750 	    } else {
751 		if (dx == 0) {	/* special case 1 */
752 		    px = ox;
753 		    py = (dy > 0) ? h - 1 : 0;
754 		} else if (dy == 0) {	/* special case 2 */
755 		    py = oy;
756 		    px = (dx > 0) ? w - 1 : 0;
757 		} else {	/* general case */
758 		    gradient = ((double) dy) / dx;
759 		    if (fabs(gradient) < fabs(diagonal)) {
760 			px = (dx > 0) ? w - 1 : 0;
761 			py = oy + ((px - ox) * dy) / dx;
762 		    } else {
763 			py = (dy > 0) ? h - 1 : 0;
764 			px = ox + ((py - oy) * dx) / dy;
765 		    }
766 		}
767 
768 		/*
769 		 * given O(ox,oy), P(px,py), and A(x,y), compute
770 		 *   |AO|/|PO|
771 		 */
772 		ap = sqrt((double) (dx * dx) + (double) (dy * dy)) /
773 		    sqrt((double) ((px - ox) * (px - ox)) +
774 			 (double) ((py - oy) * (py - oy)));
775 
776 		rgb = ImagePixel(input, px, py);
777 		r = (1 - ap) * rSum + ap * rgb[0];
778 		g = (1 - ap) * gSum + ap * rgb[1];
779 		b = (1 - ap) * bSum + ap * rgb[2];
780 	    }
781 	    *op++ = r;
782 	    *op++ = g;
783 	    *op++ = b;
784 	}
785 	op += 6;		/* skip last on this line and first on next */
786     }
787 
788     return output;
789 }
790 
791 Image *
ImagePixelize(Image * input)792 ImagePixelize(Image * input)
793 {
794     int width = input->width, height = input->height;
795     Image *output = ImageNew(width, height);
796     unsigned char *op, *rgb;
797     int x, y, xx, yy, n;
798     int rSum, gSum, bSum;
799     int xsize, ysize;
800 
801 
802     xsize = ImgProcessInfo.pixelizeXSize;
803     ysize = ImgProcessInfo.pixelizeYSize;
804 
805     if (xsize > width)
806 	xsize = width;
807     if (ysize > height)
808 	ysize = height;
809     n = xsize * ysize;
810 
811     for (y = 0; y < height; y += ysize) {
812 	for (x = 0; x < width; x += xsize) {
813 	    /*
814 	     *	compute average of pixels inside megapixel
815 	     */
816 	    rSum = gSum = bSum = 0;
817 	    for (yy = y; yy < y + ysize; yy++) {
818 		if (yy >= height)
819 		    continue;
820 		for (xx = x; xx < x + xsize; xx++) {
821 		    if (xx >= width)
822 			continue;
823 		    rgb = ImagePixel(input, xx, yy);
824 		    rSum += rgb[0];
825 		    gSum += rgb[1];
826 		    bSum += rgb[2];
827 		}
828 	    }
829 	    rSum /= n;
830 	    gSum /= n;
831 	    bSum /= n;
832 	    /*
833 	     * replace each pixel in megapixel with average
834 	     */
835 	    for (yy = y; yy < y + ysize; yy++) {
836 		if (yy >= height)
837 		    continue;
838 		for (xx = x; xx < x + xsize; xx++) {
839 		    if (xx >= width)
840 			continue;
841 		    op = ImagePixel(output, xx, yy);
842 		    *op++ = rSum;
843 		    *op++ = gSum;
844 		    *op = bSum;
845 		}
846 	    }
847 	}
848 
849 	if (y % 16 == 0)
850 	    StateTimeStep();
851     }
852 
853     /* if any incomplete megapixels are left, copy them to the output */
854     for (x = (width / xsize) * xsize; x < width; ++x)
855 	for (y = 0; y < height; ++y) {
856 	    rgb = ImagePixel(input, x, y);
857 	    op = ImagePixel(output, x, y);
858 	    *op++ = *rgb++;
859 	    *op++ = *rgb++;
860 	    *op = *rgb;
861 	}
862     for (y = (height / ysize) * ysize; y < height; ++y)
863 	for (x = 0; x < width; ++x) {
864 	    rgb = ImagePixel(input, x, y);
865 	    op = ImagePixel(output, x, y);
866 	    *op++ = *rgb++;
867 	    *op++ = *rgb++;
868 	    *op = *rgb;
869 	}
870 
871     return output;
872 }
873 
874 
875 #define SWAP(a, b)	{ int t = (a); (a) = (b); (b) = t; }
876 
877 Image *
ImageDespeckle(Image * input)878 ImageDespeckle(Image * input)
879 {
880     int width = input->width, height = input->height;
881     Image *output = ImageNew(width, height);
882     unsigned char *op, *rgb;
883     int x, y, xx, yy, mask, mask2, i, j, k, l;
884     int *ra, *ga, *ba;
885 
886     mask = ImgProcessInfo.despeckleMask;
887     if (mask > width)
888 	mask = width;
889     if (mask > height)
890 	mask = height;
891     mask2 = mask / 2;
892 
893     /* arrays for storing pixels inside mask */
894     ra = xmalloc(mask * mask * sizeof(int));
895     ga = xmalloc(mask * mask * sizeof(int));
896     ba = xmalloc(mask * mask * sizeof(int));
897 
898     op = output->data;
899     for (y = 0; y < height; ++y) {
900 	for (x = 0; x < width; ++x) {
901 	    i = 0;
902 	    for (yy = MAX(0, y - mask2); yy < MIN(height, y + mask2); ++yy)
903 		for (xx = MAX(0, x - mask2); xx < MIN(width, x + mask2); ++xx) {
904 		    rgb = ImagePixel(input, xx, yy);
905 		    ra[i] = *rgb++;
906 		    ga[i] = *rgb++;
907 		    ba[i] = *rgb;
908 		    ++i;
909 		}
910 	    /*
911 	     * now find median by (shell-)sorting the arrays and
912 	     * picking the center value
913 	     */
914 	    for (j = i / 2; j > 0; j = j / 2)
915 		for (k = j; k < i; k++) {
916 		    for (l = k - j; l >= 0 && ra[l] > ra[l + j]; l -= j)
917 			SWAP(ra[l], ra[l + j]);
918 		    for (l = k - j; l >= 0 && ga[l] > ga[l + j]; l -= j)
919 			SWAP(ga[l], ga[l + j]);
920 		    for (l = k - j; l >= 0 && ba[l] > ba[l + j]; l -= j)
921 			SWAP(ba[l], ba[l + j]);
922 		}
923 	    if (i & 1) {	/* uneven number of data points */
924 		*op++ = ra[i / 2];
925 		*op++ = ga[i / 2];
926 		*op++ = ba[i / 2];
927 	    } else {		/* even, take average */
928 		*op++ = (ra[i / 2 - 1] + ra[i / 2]) / 2;
929 		*op++ = (ga[i / 2 - 1] + ga[i / 2]) / 2;
930 		*op++ = (ba[i / 2 - 1] + ba[i / 2]) / 2;
931 	    }
932 	}
933 	if (y % 16 == 0)
934 	    StateTimeStep();
935     }
936 
937     free(ra);
938     free(ga);
939     free(ba);
940 
941     return output;
942 }
943 
944 
945 /*
946  * Normalize the contrast of an image.
947  */
948 Image *
ImageNormalizeContrast(Image * input)949 ImageNormalizeContrast(Image * input)
950 {
951     unsigned char *p;
952     int *hist;
953     int x, y, w, h, i, max = 0, cumsum, limit;
954     Image *output = ImageNew(input->width, input->height);
955     unsigned char *op = output->data;
956     int blackpcnt, blackval, whitepcnt, whiteval;
957 
958 
959     blackpcnt = ImgProcessInfo.contrastB;
960     whitepcnt = 100 - ImgProcessInfo.contrastW;
961 
962     hist = histogram(input);
963 
964     w = input->width;
965     h = input->height;
966 
967     /* Find lower knee point in histogram to determine 'black' threshold. */
968     cumsum = 0;
969     limit = w * h * blackpcnt / 100;
970     for (i = 0; i < 256; ++i)
971 	if ((cumsum += hist[i]) > limit)
972 	    break;
973     blackval = i;
974 
975     /* Likewise for 'white' threshold. */
976     cumsum = 0;
977     limit = w * h * whitepcnt / 100;
978     for (i = 255; i > 0; --i) {
979 	if ((max == 0) && hist[i])
980 	    max = i;	/* max is index of highest non-zero entry */
981 	if ((cumsum += hist[i]) > limit)
982 	    break;
983     }
984     whiteval = i;
985 
986     if (whiteval == blackval) {
987         for (y = 0; y < h; y++) {
988 	    for (x = 0; x < w; x++) {
989 	        p = ImagePixel(input, x, y);
990 	        *op++ = *p++;
991 	        *op++ = *p++;
992 	        *op++ = *p;
993 	    }
994 	}
995         free(hist);
996         return output;
997     }
998 
999     /*
1000      * Now create a histogram with a linear ramp
1001      * from (blackval, 0) to (whiteval, max)
1002      */
1003     for (i = 0; i < blackval; ++i)
1004 	hist[i] = 0;
1005     for (; i <= whiteval; ++i)
1006 	hist[i] = max * (i - blackval) / (whiteval - blackval);
1007     for (; i < 256; ++i)
1008 	hist[i] = max;
1009 
1010     for (y = 0; y < h; y++) {
1011 	for (x = 0; x < w; x++) {
1012 	    p = ImagePixel(input, x, y);
1013 	    *op++ = hist[*p++];
1014 	    *op++ = hist[*p++];
1015 	    *op++ = hist[*p];
1016 	}
1017     }
1018     free(hist);
1019 
1020     return output;
1021 }
1022 
1023 /*
1024  * Build histogram data for the image.
1025  */
1026 static int *
histogram(Image * input)1027 histogram(Image * input)
1028 {
1029     unsigned char *p;
1030     int x, y, w, h, i, r, g, b, *hist;
1031 
1032     w = input->width;
1033     h = input->height;
1034 
1035     /* Make a table of 256 zeros plus one sentinel */
1036     hist = xmalloc(257 * sizeof(int));
1037     for (i = 0; i <= 256; ++i)
1038 	hist[i] = 0;
1039 
1040     /*
1041      * Build histogram of intensity values.
1042      */
1043     for (y = 0; y < h; y++) {
1044 	for (x = 0; x < w; x++) {
1045 	    p = ImagePixel(input, x, y);
1046 	    r = *p++;
1047 	    g = *p++;
1048 	    b = *p;
1049 	    i = GreyScale(r, g, b);
1050 	    ++hist[i];
1051 	}
1052     }
1053     return hist;
1054 }
1055 
1056 #if 0
1057 
1058 #define HIST_H	  120		/* Height of histogram */
1059 #define HIST_W	  256		/* Width of histogram */
1060 #define HIST_B	    2		/* Horizontal border width for histogram */
1061 #define HIST_R	   12		/* Height of ruler below histogram */
1062 #define HIST_T	    9		/* Height of tick marks on ruler */
1063 #define LIGHTGREY 200		/* Greyscale level used for bars */
1064 #define MIDGREY	  255		/* Greyscale level used for ruler */
1065 #define DARKGREY  120		/* Greyscale level used for background */
1066 
1067 /*
1068  * Create an image (width 256, height HIST_H) depicting the histogram data.
1069  */
1070 Image *
1071 HistogramImage(char *hist)
1072 {
1073     unsigned char *p;
1074     int x, y, i;
1075     int max, e:
1076     unsigned char *ihist;
1077     Image *output = ImageNewGrey(HIST_W + 2 * HIST_B, HIST_H + HIST_R);;
1078 
1079     max = 0;
1080     ihist = xmalloc(128 * sizeof(int));
1081     for (i = 0; i < 128; ++i) {
1082 	e = ihist[i] = hist[i * 2] + hist[i * 2 + 1];
1083 	if (e > max)
1084 	    max = e;
1085     }
1086     /*
1087      * Now create histogram image.
1088      * Only display 128 bins to make the histogram easier to read.
1089      * Leave a border of a few pixels in each side.
1090      */
1091 
1092     memset(output->data, DARKGREY, (HIST_W + 2 * HIST_B) * (HIST_H + HIST_R));
1093     for (x = 0; x < 128; ++x) {
1094 	i = ihist[x] * (HIST_H - 1) / max;
1095 	for (y = HIST_H - 1 - i; y < HIST_H - 1; y++) {
1096 	    p = output->data + y * (HIST_W + 2 * HIST_B) + x * 2 + HIST_B;
1097 	    *p++ = LIGHTGREY;
1098 	    *p = LIGHTGREY;
1099 	}
1100     }
1101     /* Ruler */
1102     memset(output->data + HIST_H * (HIST_W + 2 * HIST_B) + HIST_B,
1103 	   MIDGREY, HIST_W);
1104     for (x = 0; x < 5; ++x) {
1105 	int tick[] =
1106 	{0, HIST_W / 4, HIST_W / 2, HIST_W * 3 / 4, HIST_W - 1};
1107 
1108 	for (y = 0; y < HIST_T; y++) {
1109 	    p = output->data + (y + HIST_H + 1) * (HIST_W + 2 * HIST_B)
1110 		+ tick[x] + HIST_B;
1111 	    *p = MIDGREY;
1112 	}
1113     }
1114     free(ihist);
1115 
1116     return output;
1117 }
1118 #endif
1119 
1120 /*
1121  * Tilt an image.
1122  */
1123 
1124 /*
1125  * Compute the interpolated RGB values of a 1 x 1 pixel centered
1126  * at (x,y) and store these values in op[0] trough op[2].
1127  * The interpolation is based on weighting the RGB values proportionally
1128  * to the overlapping areas.
1129  */
1130 static void
interpolate(double x,double y,Image * input,unsigned char * op)1131 interpolate(double x, double y, Image * input, unsigned char *op)
1132 {
1133     int xl, xh, yl, yh;
1134     double a, b, c, d, A, B, C, D;
1135     unsigned char *plh, *phh, *pll, *phl;
1136 
1137     xl = floor(x);
1138     xh = ceil(x);
1139     yl = floor(y);
1140     yh = ceil(y);
1141 
1142     if (xl < 0) xl = 0;
1143     if (yl < 0) yl = 0;
1144     if (xh >= input->width) xh = input->width - 1;
1145     if (yh >= input->height) yh = input->height - 1;
1146 
1147     pll = ImagePixel(input, xl, yl);
1148 
1149     if (xh == xl) {
1150 	if (yh == yl) {
1151 	    /* pixel coincides with one pixel */
1152 	    *op++ = *pll++;
1153 	    *op++ = *pll++;
1154 	    *op++ = *pll++;
1155 	} else {
1156 	    /* pixel overlaps with two pixels on top of each other */
1157 	    A = y - yl;
1158 	    B = yh - y;
1159             plh = ImagePixel(input, xl, yh);
1160 	    *op++ = A * *plh++ + B * *pll++;
1161 	    *op++ = A * *plh++ + B * *pll++;
1162 	    *op++ = A * *plh++ + B * *pll++;
1163 	}
1164     } else if (yh == yl) {
1165 	/* pixel overlaps with two side-by-side pixels */
1166 	A = x - xl;
1167 	B = xh - x;
1168 	phl = ImagePixel(input, xh, yl);
1169 	*op++ = A * *phl++ + B * *pll++;
1170 	*op++ = A * *phl++ + B * *pll++;
1171 	*op++ = A * *phl++ + B * *pll++;
1172     } else {
1173 	/* general case: pixel overlaps all four neighbour pixels */
1174 	a = xh - x;
1175 	b = y - yl;
1176 	c = x - xl;
1177 	d = yh - y;
1178 	A = a * b;
1179 	B = b * c;
1180 	C = a * d;
1181 	D = c * d;
1182 	plh = ImagePixel(input, xl, yh);
1183 	phl = ImagePixel(input, xh, yl);
1184 	phh = ImagePixel(input, xh, yh);
1185 	*op++ = A * *plh++ + B * *phh++ + C * *pll++ + D * *phl++;
1186 	*op++ = A * *plh++ + B * *phh++ + C * *pll++ + D * *phl++;
1187 	*op++ = A * *plh++ + B * *phh++ + C * *pll++ + D * *phl++;
1188     }
1189 }
1190 
1191 Image *
ImageDistort(Image * input)1192 ImageDistort(Image * input)
1193 {
1194     int x, y, xi, yi;
1195     int width = input->width, height = input->height;
1196     Image *output;
1197     unsigned char *p, *op;
1198     double a, b, sx, sy, sx2, sy2, sp, w, h;
1199 
1200     a = ImgProcessInfo.distortX;
1201     b = ImgProcessInfo.distortY;
1202     w = width - 1.0;
1203     h = height - 1.0;
1204 
1205     if (width<=1 || height<=1) return input;
1206 
1207     output = ImageNew(width, height);
1208 
1209     for (y = 0; y < height; y++) {
1210         sy = (double)y / h;
1211         sy2 = 1.0 - sy;
1212         sy2 = sy*sy*sy2*sy2;
1213 	for (x = 0; x < width; x++) {
1214 	    sx = (double)x / w;
1215             sx2 = 1.0 - sx;
1216             sx2 = sx*sx*sx2*sx2;
1217             sp = sx2 * sy2;
1218             xi = (int) (w * (sx + a * sp) + 0.5);
1219             CLAMP(0, xi, width-1);
1220 	    yi = (int) (h * (sy + b * sp) + 0.5);
1221             CLAMP(0, yi, height-1);
1222             p = ImagePixel(input, xi, yi);
1223 	    op = ImagePixel(output, x, y);
1224 	    *op++ = *p++;
1225 	    *op++ = *p++;
1226 	    *op = *p;
1227 	}
1228     }
1229 
1230     return output;
1231 }
1232 
1233 Image *
ImageProjTransform(Image * input)1234 ImageProjTransform(Image * input)
1235 {
1236     int x, y, br, bg, bb;
1237     int width = input->width, height = input->height;
1238     Image *output;
1239     unsigned char *op;
1240     double xs, ys, xt, yt, d;
1241     double X1, Y1, X2, Y2;
1242     double a11, a12, a21, a22;
1243 
1244     X1 = width * (ImgProcessInfo.projX1 / 100.0);
1245     Y1 = height * (ImgProcessInfo.projY1 / 100.0);
1246 
1247     X2 = width * (ImgProcessInfo.projX2 / 100.0) - X1;
1248     Y2 = height * (ImgProcessInfo.projY2 / 100.0) - Y1;
1249 
1250     d = X2*X2 + Y2*Y2;
1251 
1252     if (ImgProcessInfo.linearDX == 0 || ImgProcessInfo.linearDY == 0 || d == 0)
1253         return input;
1254 
1255     X2 = X2/d;
1256     Y2 = Y2/d;
1257 
1258     d = ImgProcessInfo.linearAngle * M_PI / 180.0;
1259     xs = cos(d);
1260     ys = sin(d);
1261     xt = 1.0 / ImgProcessInfo.linearDX;
1262     yt = 1.0 / ImgProcessInfo.linearDY;
1263     d = ImgProcessInfo.linearIncli;
1264     a11 = xs * xt + ys * d;
1265     a12 = xs * d - ys * xt;
1266     a21 = ys * yt;
1267     a22 = xs * yt;
1268 
1269     output = ImageNew(width, height);
1270 
1271     /* Get RGB values of background colour */
1272     br = ImgProcessInfo.background->red / 256;
1273     bg = ImgProcessInfo.background->green / 256;
1274     bb = ImgProcessInfo.background->blue / 256;
1275 
1276     op = output->data;
1277     for (x = 0; x < width * height; ++x) {
1278 	*op++ = br;
1279 	*op++ = bg;
1280 	*op++ = bb;
1281     }
1282 
1283     for (y = 0; y < height; y++) {
1284 	for (x = 0; x < width; x++) {
1285 	    op = ImagePixel(output, x, y);
1286 	    /* find the input coords corresponding to output coords (x,y)
1287                (X1,Y1) fixed point, (X2,Y2) sent to infinity */
1288             xs = x - X1;
1289             ys = y - Y1;
1290             d =  1.0 - (X2 * xs + Y2 * ys);
1291             if (fabs(d)> 1E-5) {
1292 	        xs = xs / d;
1293 	        ys = ys / d;
1294                 xt = a11 * xs + a12 * ys + X1;
1295                 yt = a21 * xs + a22 * ys + Y1;
1296 	        if ((xt > -1) && (xt < width) && (yt > -1.0) && (yt < height)) {
1297 #if 0
1298 		    unsigned char *p;
1299 		    p = ImagePixel(input, xp, yp);
1300 		    *op++ = *p++;
1301 		    *op++ = *p++;
1302 		    *op = *p;
1303 #else
1304 		    interpolate(xt, yt, input, op);
1305 #endif
1306 		}
1307 	    }
1308 	}
1309     }
1310 
1311     return output;
1312 }
1313 
1314 /*
1315  * Produce the 'solarization' effect seen when exposing a
1316  * photographic film to light during the development process.
1317  * Done here by inverting all pixels above threshold level.
1318  */
1319 Image *
ImageSolarize(Image * input)1320 ImageSolarize(Image * input)
1321 {
1322     Image *output = ImageNewCmap(input->width, input->height, input->cmapSize);
1323     int i;
1324     unsigned char *ip, *op;
1325     int count, limit;
1326 
1327     limit = ImgProcessInfo.solarizeThreshold * 255 / 100;
1328 
1329     /*
1330     **	 If the input has a colormap, just tweak that.
1331      */
1332     if (input->cmapSize != 0) {
1333 	ip = input->cmapData;
1334 	op = output->cmapData;
1335 	count = input->cmapSize;
1336 
1337 	memcpy(output->data, input->data,
1338 	     sizeof(char) * input->scale * input->width * input->height);
1339     } else {
1340 	ip = input->data;
1341 	op = output->data;
1342 	count = input->width * input->height;
1343     }
1344 
1345     for (i = 0; i < count; i++) {
1346 	*op++ = (*ip > limit) ? 255 - *ip++ : *ip++;
1347 	*op++ = (*ip > limit) ? 255 - *ip++ : *ip++;
1348 	*op++ = (*ip > limit) ? 255 - *ip++ : *ip++;
1349     }
1350 
1351     return output;
1352 }
1353 
1354 /*
1355  * Colourmap quantization. Hacked from ppmqvga.c, which is part of Netpbm and
1356  * was originally written by Lyle Rains (lrains@netcom.com) and Bill Davidsen
1357  * (davidsen@crd.ge.com).
1358  * You are probably not supposed to understand this.
1359  */
1360 
1361 #define RED_BITS   5
1362 #define GREEN_BITS 6
1363 #define BLUE_BITS  5
1364 
1365 #define MAX_RED	   (1 << RED_BITS)
1366 #define MAX_GREEN  (1 << GREEN_BITS)
1367 #define MAX_BLUE   (1 << BLUE_BITS)
1368 
1369 #define MAXWEIGHT  128
1370 #define STDWEIGHT_DIV  (2 << 8)
1371 #define STDWEIGHT_MUL  (2 << 10)
1372 #define GAIN	   4
1373 
1374 static int r, g, b, clutx, rep_threshold, rep_weight, dr, dg, db;
1375 static int *color_cube, ncolors;
1376 static unsigned char *clut;
1377 
1378 #define CUBEINDEX(r,g,b)  (r)*(MAX_GREEN*MAX_BLUE) + (g)*MAX_BLUE + (b)
1379 
1380 static void
diffuse(void)1381 diffuse(void)
1382 {
1383     int _7_32nds, _3_32nds, _1_16th;
1384 
1385     if (clutx < ncolors) {
1386 	if (color_cube[CUBEINDEX(r, g, b)] > rep_threshold) {
1387 	    clut[clutx * 4 + 0] = ((2 * r + 1) * 256) / (2 * MAX_RED);
1388 	    clut[clutx * 4 + 1] = ((2 * g + 1) * 256) / (2 * MAX_GREEN);
1389 	    clut[clutx * 4 + 2] = ((2 * b + 1) * 256) / (2 * MAX_BLUE);
1390 	    ++clutx;
1391 	    color_cube[CUBEINDEX(r, g, b)] -= rep_weight;
1392 	}
1393 	_7_32nds = (7 * color_cube[CUBEINDEX(r, g, b)]) / 32;
1394 	_3_32nds = (3 * color_cube[CUBEINDEX(r, g, b)]) / 32;
1395 	_1_16th = color_cube[CUBEINDEX(r, g, b)] - 3 * (_7_32nds + _3_32nds);
1396 	color_cube[CUBEINDEX(r, g, b)] = 0;
1397 	/* spread error evenly in color space. */
1398 	color_cube[CUBEINDEX(r, g, b + db)] += _7_32nds;
1399 	color_cube[CUBEINDEX(r, g + dg, b)] += _7_32nds;
1400 	color_cube[CUBEINDEX(r + dr, g, b)] += _7_32nds;
1401 	color_cube[CUBEINDEX(r, g + dg, b + db)] += _3_32nds;
1402 	color_cube[CUBEINDEX(r + dr, g, b + db)] += _3_32nds;
1403 	color_cube[CUBEINDEX(r + dr, g + dg, b)] += _3_32nds;
1404 	color_cube[CUBEINDEX(r + dr, g + dg, b + db)] += _1_16th;
1405 
1406 	/*
1407 	 * Conserve the error at edges if possible
1408 	 * (which it is, except the last pixel)
1409 	 */
1410 	if (color_cube[CUBEINDEX(r, g, b)] != 0) {
1411 	    if (dg != 0)
1412 		color_cube[CUBEINDEX(r, g + dg, b)] +=
1413 		    color_cube[CUBEINDEX(r, g, b)];
1414 	    else if (dr != 0)
1415 		color_cube[CUBEINDEX(r + dr, g, b)] +=
1416 		    color_cube[CUBEINDEX(r, g, b)];
1417 	    else if (db != 0)
1418 		color_cube[CUBEINDEX(r, g, b) + db] +=
1419 		    color_cube[CUBEINDEX(r, g, b)];
1420 	    else
1421 		fprintf(stderr, "%s\n", msgText[LOST_ERROR_TERM]);
1422 	}
1423     }
1424     color_cube[CUBEINDEX(r, g, b)] = -1;
1425 }
1426 
1427 /*
1428 ** Find representative color nearest to requested color.  Check color cube
1429 ** for a cached color index.  If not cached, compute nearest and cache result.
1430  */
1431 static int
nearest_color(unsigned char * p)1432 nearest_color(unsigned char *p)
1433 {
1434     register unsigned char *test;
1435     register unsigned i;
1436     unsigned long min_dist_sqd, dist_sqd;
1437     int nearest = 0;
1438     int *cache;
1439     int r, g, b;
1440 
1441     r = *p++;
1442     g = *p++;
1443     b = *p;
1444     cache = &(color_cube[CUBEINDEX((r << RED_BITS) / 256,
1445 				   (g << GREEN_BITS) / 256,
1446 				   (b << BLUE_BITS) / 256)]);
1447     if (*cache >= 0)
1448 	return *cache;
1449     min_dist_sqd = ~0;
1450     for (i = 0; i < ncolors; ++i) {
1451 	test = &clut[i * 4];
1452 	dist_sqd =
1453 	    3 * (r - test[0]) * (r - test[0]) +
1454 	    4 * (g - test[1]) * (g - test[1]) +
1455 	    2 * (b - test[2]) * (b - test[2]);
1456 	if (dist_sqd < min_dist_sqd) {
1457 	    nearest = i;
1458 	    min_dist_sqd = dist_sqd;
1459 	}
1460     }
1461     return (*cache = nearest);
1462 }
1463 
1464 void
GammaScale(unsigned char * scale,double f)1465 GammaScale(unsigned char * scale, double f)
1466 {
1467     int c;
1468     scale[0] = 0;
1469     scale[255] = 255;
1470     if (f > 12) {
1471         for (c = 1; c <= 254; c++) scale[c] = 0;
1472         return;
1473     }
1474     if (f < -12) {
1475         for (c = 1; c <= 254; c++) scale[c] = 255;
1476         return;
1477     }
1478     /* logarithmic gamma scale */
1479     f = pow(2.0, -f);
1480     for (c = 1; c <= 254; c++)
1481         scale[c] = (int)(255.5 * pow(c/255.0, f));
1482 }
1483 
1484 Image *
ImageGammaCorrection(Image * input)1485 ImageGammaCorrection(Image * input)
1486 {
1487     int w = input->width, h = input->height;
1488     Image *output;
1489     unsigned char scale[768];
1490     unsigned char *ip, *op;
1491     int c, nc, x, y, i, j;
1492     double a;
1493     double *coef = (double *)ImgProcessInfo.rgb_mat;
1494 
1495     /* find out whether output will be grey image */
1496     if (ImgProcessInfo.RGB_correction >= 3 ||
1497         (ImgProcessInfo.RGB_correction == 0 && input->isGrey) ||
1498         (ImgProcessInfo.RGB_correction == -1 && input->isGrey) ||
1499         (ImgProcessInfo.RGB_correction == 1 && input->isGrey &&
1500          ImgProcessInfo.r_gamma == ImgProcessInfo.g_gamma &&
1501 	 ImgProcessInfo.g_gamma == ImgProcessInfo.b_gamma)) {
1502         output = ImageNewGrey(w, h);
1503         nc = 1;
1504     } else {
1505         output = ImageNew(w, h);
1506         nc = 3;
1507     }
1508 
1509     if (ImgProcessInfo.RGB_correction == 0) {
1510         /* threshold */
1511         a = 255.0 / (double)(ImgProcessInfo.RGB_max-ImgProcessInfo.RGB_min);
1512         for (y = 0; y < h; y++) {
1513             op = output->data + nc*w*y;
1514 	    for (x = 0; x < w; x++) {
1515                 ip = ImagePixel(input, x, y);
1516                 for (i=0; i<nc; i++) {
1517 		    c = (int) (((int)(*ip++)-ImgProcessInfo.RGB_min) * a + 0.5);
1518                     if (c < 0) c = 0;
1519                     if (c > 255) c = 255;
1520                     *op++ = c;
1521 		}
1522 	    }
1523 	}
1524         return output;
1525     }
1526 
1527     if (ImgProcessInfo.RGB_correction == 1 ||
1528         ImgProcessInfo.RGB_correction == -1) {
1529         /* gamma correction */
1530         GammaScale(scale, ImgProcessInfo.r_gamma);
1531         if (nc == 3) {
1532             if (ImgProcessInfo.RGB_correction == -1) {
1533 	        memcpy(scale+256, scale, 256);
1534 	        memcpy(scale+512, scale, 256);
1535 	    } else {
1536                 GammaScale(scale+256, ImgProcessInfo.g_gamma);
1537                 GammaScale(scale+512, ImgProcessInfo.b_gamma);
1538 	    }
1539         }
1540         for (y = 0; y < h; y++) {
1541             op = output->data + nc*w*y;
1542             if (nc == 1)
1543 	        for (x = 0; x < w; x++) {
1544                     ip = ImagePixel(input, x, y);
1545 	            *op++ = scale[*ip++];
1546 	        }
1547             else
1548   	        for (x = 0; x < w; x++) {
1549                     ip = ImagePixel(input, x, y);
1550  	            for (c=0; c<nc; c++)
1551 	                *op++ = scale[(c<<8) + *ip++];
1552 	        }
1553         }
1554         return output;
1555     }
1556 
1557     if (ImgProcessInfo.RGB_correction == 2) {
1558         /* linear correction */
1559         for (y = 0; y < h; y++) {
1560 	    /* This is a color image then */
1561             op = output->data + 3*w*y;
1562 	    for (x = 0; x < w; x++) {
1563                 ip = ImagePixel(input, x, y);
1564                 for (i=0; i<=2; i++) {
1565 		    j = i<<2;
1566 		    a = (ip[0]*coef[j]+ip[1]*coef[j+1]+ip[2]*coef[j+2])/100.0
1567                         + coef[j+3] + 0.5;
1568                     c = (int) a;
1569                     if (c < 0) c = 0;
1570                     if (c > 255) c = 255;
1571                     *op++ = c;
1572 		}
1573 	    }
1574 	}
1575         return output;
1576     }
1577 
1578     if (ImgProcessInfo.RGB_correction >= 3 &&
1579         ImgProcessInfo.RGB_correction <= 5) {
1580         /* R,G,B transform */
1581         c = ImgProcessInfo.RGB_correction - 3;
1582         for (y = 0; y < h; y++) {
1583             op = output->data + w*y;
1584 	    for (x = 0; x < w; x++) {
1585                 ip = ImagePixel(input, x, y);
1586 	        *op++ = ip[c];
1587 	    }
1588 	}
1589         output->isGrey = False;
1590         memset(output->cmapData, 0, 3*256);
1591         for (x = 0; x < 256; x++)
1592 	    output->cmapData[3*x+c] = x;
1593         return output;
1594     }
1595 
1596     if (ImgProcessInfo.RGB_correction == 6) {
1597         /* maximum intensity */
1598         for (y = 0; y < h; y++) {
1599             op = output->data + w*y;
1600 	    for (x = 0; x < w; x++) {
1601                 ip = ImagePixel(input, x, y);
1602 	        *op++ = MAX(MAX(ip[0], ip[1]), ip[2]);
1603 	    }
1604 	}
1605         return output;
1606     }
1607 
1608     if (ImgProcessInfo.RGB_correction == 7) {
1609         /* minimum intensity */
1610         for (y = 0; y < h; y++) {
1611             op = output->data + w*y;
1612 	    for (x = 0; x < w; x++) {
1613                 ip = ImagePixel(input, x, y);
1614 	        *op++ = MIN(MIN(ip[0], ip[1]), ip[2]);
1615 	    }
1616 	}
1617         return output;
1618     }
1619 
1620     if (ImgProcessInfo.RGB_correction == 8) {
1621         /* find mask with respect to background */
1622         for (y = 0; y < h; y++) {
1623             op = output->data + w*y;
1624 	    for (x = 0; x < w; x++) {
1625                 ip = ImagePixel(input, x, y);
1626                 if (memcmp(ip, Global.bg, 3))
1627                     *op++ = 0xff;
1628                 else
1629                     *op++ = 0;
1630 	    }
1631 	}
1632         return output;
1633     }
1634 }
1635 
1636 Image *
ImageFloydSteinberg(Image * input)1637 ImageFloydSteinberg(Image * input)
1638 {
1639     int w = input->width, h = input->height;
1640     Image *output;
1641     int d, wp, c, nc, nd, p, x, y, error = 0;
1642     unsigned char *op;
1643     unsigned char subst[256];
1644     unsigned char scale[256];
1645 
1646     if (input->isGrey) {
1647         nc = 1;
1648         output = ImageNewGrey(w, h);
1649     } else {
1650         nc = 3;
1651         output = ImageNew(w, h);
1652     }
1653     wp = nc * w;
1654 
1655     ImageCopyData(input, output);
1656 
1657     /* calculate substitution values */
1658     x = ImgProcessInfo.FSsteps - 1;
1659     p = ImgProcessInfo.FSmidrange - 1;
1660     GammaScale(scale, ImgProcessInfo.FSgamma);
1661 
1662     for (c = 0; c <= 255; c++) {
1663         y = (c * x + p) / 255;
1664         subst[c] = (unsigned char)((255 * y  + x/2) / x);
1665     }
1666 
1667     for (c = 0; c < nc; c++)
1668     for (y = 0; y < h; y++) {
1669         d = 1 - ((y&1)<<1);
1670         nd = d*nc;
1671         op = output->data + wp*y + c;
1672         if (d == -1)
1673             op += wp - nc;
1674         for (x = 0; x < w; x++) {
1675 	    p = (int)(scale[*op]);
1676             *op = subst[p];
1677             error = p - (int)(*op);
1678             if (!error) goto fast_done;
1679 
1680 	    /*  error diffusion */
1681             if (x < w-1) {
1682 	        /* Right pixel */
1683 	        p = (int) op[nd] + (int) (error * 7/16);
1684 	        if (p < 0) p = 0;
1685 	        if (p > 255) p = 255;
1686                 op[nd] = p;
1687 	    }
1688             if (y < h-1) {
1689 	        if (x > 0) {
1690 		    /* Bottom left pixel */
1691 		    p = (int) op[wp-nd] + (int) (error * 3/16);
1692 	            if (p < 0) p = 0;
1693 	            if (p > 255) p = 255;
1694 		    op[wp-nd] = p;
1695 		}
1696 	        /* Bottom pixel */
1697 	        p = (int) op[wp] + (int) (error * 5/16);
1698 	        if (p < 0) p = 0;
1699 	        if (p > 255) p = 255;
1700 	        op[wp] = p;
1701 	        if (x < w-1) {
1702 	            /* Bottom right pixel */
1703 	            p = (int) op[wp+nd] + (int) (error / 16);
1704 	            if (p < 0) p = 0;
1705 	            if (p > 255) p = 255;
1706 	            op[wp+nd] = p;
1707 		}
1708 	    }
1709 	  fast_done:
1710             op += nd;
1711 	}
1712     }
1713 
1714     return output;
1715 }
1716 
1717 Image *
ImageQuantize(Image * input)1718 ImageQuantize(Image * input)
1719 {
1720     int w = input->width, h = input->height;
1721     Image *output = ImageNew(w, h);
1722     unsigned char *op = output->data;
1723     int *weight_convert;
1724     int k, x, y, i, j, nearest;
1725     int total_weight, cum_weight[MAX_GREEN];
1726     int *erropt;
1727     int *errP;
1728     unsigned char *p, *clutP;
1729 
1730 
1731     ncolors = ImgProcessInfo.quantizeColors;
1732 
1733     /* Make a color cube, initialized to zeros */
1734     color_cube = calloc(MAX_RED * MAX_GREEN * MAX_BLUE, sizeof(int));
1735     clut = calloc(ncolors * 4, sizeof(int));
1736     erropt = calloc(ncolors * 4, sizeof(int));
1737 
1738     clutx = 0;
1739 
1740     /* Count all occurrences of each color */
1741     for (y = 0; y < h; ++y)
1742 	for (x = 0; x < w; ++x) {
1743 	    p = ImagePixel(input, x, y);
1744 	    r = p[0] / (256 / MAX_RED);
1745 	    g = p[1] / (256 / MAX_GREEN);
1746 	    b = p[2] / (256 / MAX_BLUE);
1747 	    ++color_cube[CUBEINDEX(r, g, b)];
1748 	}
1749 
1750     /* Initialize logarithmic weighting table */
1751     weight_convert = xmalloc(MAXWEIGHT * sizeof(int));
1752     weight_convert[0] = 0;
1753     for (i = 1; i < MAXWEIGHT; ++i) {
1754 	weight_convert[i] = (int) (100.0 * log((double) i));
1755     }
1756 
1757     k = w * h;
1758     if ((k /= STDWEIGHT_DIV) == 0)
1759 	k = 1;
1760     total_weight = i = 0;
1761     for (g = 0; g < MAX_GREEN; ++g) {
1762 	for (r = 0; r < MAX_RED; ++r) {
1763 	    for (b = 0; b < MAX_BLUE; ++b) {
1764 		register int weight;
1765 		/* Normalize the weights, independent of picture size. */
1766 		weight = color_cube[CUBEINDEX(r, g, b)] * STDWEIGHT_MUL;
1767 		weight /= k;
1768 		if (weight)
1769 		    ++i;
1770 		if (weight >= MAXWEIGHT)
1771 		    weight = MAXWEIGHT - 1;
1772 		total_weight += (color_cube[CUBEINDEX(r, g, b)]
1773 				 = weight_convert[weight]);
1774 	    }
1775 	}
1776 	cum_weight[g] = total_weight;
1777     }
1778     rep_weight = total_weight / ncolors;
1779 
1780     /* Magic foo-foo dust here.	 What IS the correct way to select threshold? */
1781     rep_threshold = total_weight * (28 + 110000 / i) / 95000;
1782 
1783     /*
1784      * Do a 3-D error diffusion dither on the data in the color cube
1785      * to select the representative colors.  Do the dither back and forth in
1786      * such a manner that all the error is conserved (none lost at the edges).
1787      */
1788 
1789     dg = 1;
1790     for (g = 0; g < MAX_GREEN; ++g) {
1791 	dr = 1;
1792 	for (r = 0; r < MAX_RED; ++r) {
1793 	    db = 1;
1794 	    for (b = 0; b < MAX_BLUE - 1; ++b)
1795 		diffuse();
1796 	    db = 0;
1797 	    diffuse();
1798 	    ++b;
1799 	    if (++r == MAX_RED - 1)
1800 		dr = 0;
1801 	    db = -1;
1802 	    while (--b > 0)
1803 		diffuse();
1804 	    db = 0;
1805 	    diffuse();
1806 	}
1807 	/* Modify threshold to keep rep points proportionally distributed */
1808 	if ((j = clutx - (ncolors * cum_weight[g]) / total_weight) != 0)
1809 	    rep_threshold += j * GAIN;
1810 
1811 	if (++g == MAX_GREEN - 1)
1812 	    dg = 0;
1813 	dr = -1;
1814 	while (r-- > 0) {
1815 	    db = 1;
1816 	    for (b = 0; b < MAX_BLUE - 1; ++b)
1817 		diffuse();
1818 	    db = 0;
1819 	    diffuse();
1820 	    ++b;
1821 	    if (--r == 0)
1822 		dr = 0;
1823 	    db = -1;
1824 	    while (--b > 0)
1825 		diffuse();
1826 	    db = 0;
1827 	    diffuse();
1828 	}
1829 	/* Modify threshold to keep rep points proportionally distributed */
1830 	if ((j = clutx - (ncolors * cum_weight[g]) / total_weight) != 0)
1831 	    rep_threshold += j * GAIN;
1832     }
1833 
1834     /*
1835      * Check the error associated with the use of each color, and
1836      * change the value of the color to minimize the error.
1837      */
1838     for (y = 0; y < h; ++y) {
1839 	for (x = 0; x < w; ++x) {
1840 	    p = ImagePixel(input, x, y);
1841 	    nearest = nearest_color(p);
1842 	    errP = &erropt[nearest * 4];
1843 	    clutP = &clut[nearest * 4];
1844 	    errP[0] += *p++ - clutP[0];
1845 	    errP[1] += *p++ - clutP[1];
1846 	    errP[2] += *p - clutP[2];
1847 	    ++errP[3];
1848 	}
1849     }
1850 
1851     for (i = 0; i < ncolors; ++i) {
1852 	clutP = &clut[i * 4];
1853 	errP = &erropt[i * 4];
1854 	j = errP[3];
1855 	if (j > 0)
1856 	    j *= 4;
1857 	else if (j == 0)
1858 	    j = 1;
1859 	clutP[0] += (errP[0] / j) * 4;
1860 	clutP[1] += (errP[1] / j) * 4;
1861 	clutP[2] += (errP[2] / j) * 4;
1862     }
1863 
1864     /* Reset the color cache. */
1865     for (i = 0; i < MAX_RED * MAX_GREEN * MAX_BLUE; ++i)
1866 	color_cube[i] = -1;
1867 
1868     /*
1869      * Map the colors in the image to their closest match in the new colormap.
1870      */
1871     for (y = 0; y < h; ++y) {
1872 	for (x = 0; x < w; ++x) {
1873 	    p = ImagePixel(input, x, y);
1874 	    nearest = nearest_color(p);
1875 	    clutP = &clut[nearest * 4];
1876 	    *op++ = *clutP++;
1877 	    *op++ = *clutP++;
1878 	    *op++ = *clutP;
1879 	}
1880     }
1881 
1882     free(clut);
1883     free(erropt);
1884     free(weight_convert);
1885 
1886     return output;
1887 }
1888 
1889 /*
1890  * Convert an image to grey scale.
1891  */
1892 
1893 Image *
ImageGrey(Image * input)1894 ImageGrey(Image * input)
1895 {
1896     Image *output = ImageNewGrey(input->width, input->height);
1897     unsigned char *p, *op = output->data;
1898     int x, y, v;
1899 
1900     for (y = 0; y < input->height; y++) {
1901 	for (x = 0; x < input->width; x++) {
1902 	    p = ImagePixel(input, x, y);
1903 	    v = GreyScale(p[0], p[1], p[2]);
1904 	    *op++ = v;
1905 	}
1906     }
1907     return output;
1908 }
1909 
1910 Image *
ImageBWMask(Image * input)1911 ImageBWMask(Image * input)
1912 {
1913     Image *output = ImageNewGrey(input->width, input->height);
1914     unsigned char *p, *op = output->data;
1915     int x, y, v;
1916 
1917     for (y = 0; y < input->height; y++) {
1918 	for (x = 0; x < input->width; x++) {
1919 	    p = ImagePixel(input, x, y);
1920             if (memcmp(p, Global.bg, 3)) v = 0xff; else v = 0;
1921 	    *op++ = v;
1922 	}
1923     }
1924     return output;
1925 }
1926 
1927 /*
1928  * Pipe command routines
1929  */
1930 
pipe_command(command,input,output,error)1931 void pipe_command(command, input, output, error)
1932     char *command;
1933     int input(char*, int);
1934     void output(const char*, int);
1935     void error(const char*, int);
1936 {
1937     int in_pipe[2];
1938     int out_pipe[2];
1939     int err_pipe[2];
1940     int pid, bytes;
1941     char buffer[BUFSIZE];
1942 
1943     if (pipe(in_pipe)==-1)
1944     {
1945         return;
1946     }
1947     if (pipe(out_pipe)==-1)
1948     {
1949         return;
1950     }
1951     if (pipe(err_pipe)==-1)
1952     {
1953         return;
1954     }
1955 
1956     pid = fork();
1957     if (pid==-1)
1958     {
1959         close(in_pipe[0]);
1960         close(in_pipe[1]);
1961         close(out_pipe[0]);
1962         close(out_pipe[1]);
1963         close(err_pipe[0]);
1964         close(err_pipe[1]);
1965         return;
1966     }
1967 
1968     if (!pid)
1969     {    /* child's thread */
1970         close(in_pipe[1]); /* there's no writing on input pipe! */
1971         close(out_pipe[0]); /* there's no reading of output pipe! */
1972         close(err_pipe[0]); /* there's no reading of error pipe! */
1973         /* redirect stdin */
1974         if (input)
1975         {
1976             if (dup2(in_pipe[0], fileno(stdin))==-1)
1977             {
1978                 exit(1);
1979             }
1980         }
1981         /* redirect stdout */
1982         if (output)
1983         {
1984             if (dup2(out_pipe[1], fileno(stdout))==-1)
1985             {
1986                 exit(1);
1987             }
1988         }
1989         /* redirect stderr */
1990         if (error)
1991         {
1992             if (dup2(err_pipe[1], fileno(stderr))==-1)
1993             {
1994                 exit(1);
1995             }
1996         }
1997                   system(command);
1998         exit(1);
1999     }
2000     else
2001     {    /* parent's thread */
2002         close(in_pipe[0]); /* there's no reading of input pipe! */
2003         close(out_pipe[1]); /* there's no writing on output pipe! */
2004         close(err_pipe[1]); /* there's no writing on error pipe! */
2005         /* start writing to pipe */
2006         if (input)
2007         {
2008             while((bytes=input(buffer, BUFSIZE-1))>0)
2009             {
2010                 bytes = write(in_pipe[1], buffer, bytes);
2011                 if (bytes==-1)
2012                 {
2013                     break;
2014                 }
2015             }
2016             close(in_pipe[1]);
2017         }
2018         /* start reading pipe output */
2019         if (output)
2020         {
2021             while((bytes=read(out_pipe[0], buffer, BUFSIZE-1))>0)
2022                 output(buffer, bytes);
2023             close(out_pipe[0]);
2024         }
2025         /* start reading pipe error*/
2026         if (error)
2027         {
2028             while((bytes=read(err_pipe[0], buffer, BUFSIZE-1))>0)
2029                 error(buffer, bytes);
2030             close(err_pipe[0]);
2031         }
2032     }
2033 }
2034 
append_status(char * string)2035 static void append_status(char *string)
2036 {
2037 char *ptr, *catptr;
2038 
2039     XtVaGetValues(head->cclog, XtNstring, &ptr, NULL);
2040     catptr = xmalloc(strlen(ptr)+strlen(string)+10);
2041     strcpy(catptr, ptr);
2042     strcat(catptr, "\n");
2043     strcat(catptr, string);
2044     XtVaSetValues(head->cclog, XtNstring, catptr, NULL);
2045 }
2046 
CollectOutput(const char * data,int i)2047 static void CollectOutput(const char* data, int i)
2048 {
2049     static int FlagNewLine = 1;
2050     int pos=0;
2051     char superstring[2000];
2052 
2053     while(i--)
2054     {
2055     if(data[pos] == '\n')
2056     {
2057         char tmp[2000];
2058         strncpy(tmp, data, pos);
2059         tmp[pos] = '\0';
2060         tmp[pos+1] = '\0';
2061         if (!FlagNewLine)
2062         {
2063         strcat(superstring, tmp);
2064         }
2065         else
2066         {
2067             superstring[0] = '\0';
2068         strcpy(superstring, tmp);
2069         }
2070         data = data + pos + 1;
2071         pos = 0;
2072         FlagNewLine = 1;
2073             append_status(superstring);
2074     }
2075     else
2076         pos++;
2077     }
2078     if (pos)
2079     {
2080     if (!FlagNewLine)
2081         strcat(superstring, data);
2082     else
2083         superstring[0] = '\0';
2084     FlagNewLine = 0;
2085     }
2086 }
2087 
2088 
2089 /*
2090  * User defined script procedures
2091  */
2092 
2093 static void
closeCallback(Widget w,XtPointer wlArg,XtPointer junk)2094 closeCallback(Widget w, XtPointer wlArg, XtPointer junk)
2095 {
2096    LocalInfo *info = (LocalInfo *) wlArg;
2097    PopdownMenusGlobal();
2098    XtDestroyWidget(info->shell);
2099 }
2100 
2101 static void
compileCallback(Widget w,XtPointer wlArg,XtPointer junk)2102 compileCallback(Widget w, XtPointer wlArg, XtPointer junk)
2103 {
2104    LocalInfo *info = (LocalInfo *) wlArg;
2105    FILE *fd;
2106    void (* proc)(Widget wid);
2107    Image * (* iproc)();
2108    Image * image;
2109    Screen *screen;
2110    Visual *visual;
2111    Pixmap pix;
2112    Colormap cmap;
2113    Widget paint;
2114    char *tmp, *ptr, *text;
2115    char cmd[512];
2116    char header[30];
2117    char * error;
2118    void *dl_handle = NULL;
2119    unsigned char *alpha = NULL;
2120    int i;
2121 
2122    /* create temporary name */
2123    fd= openTempFile(&tmp);
2124    fclose(fd);
2125 
2126    /* copy buffer to temporary C file */
2127    XtVaGetValues(info->program, XtNstring, &text, NULL);
2128    if (!text || !*text) {
2129       return;
2130    }
2131    sprintf(cmd, "%s.c", tmp);
2132    fd = fopen(cmd, "w");
2133    if (!fd) return;
2134 
2135    ptr = text;
2136    i = 0;
2137    while (*ptr) {
2138       fputc(*ptr, fd);
2139       if (i <= 25) {
2140          header[i] = *ptr;
2141          if (*ptr == '\n') header[i] = '\0';
2142 	 i++;
2143       }
2144       ++ptr;
2145    }
2146    fclose(fd);
2147    header[i] = '\0';
2148 
2149    info->mode = -1;
2150    if (strncasecmp(header, "/* Xpaint-image */", 18) == 0)
2151       info->mode = PREDEF_IMAGES;
2152    if (strncasecmp(header, "/* Xpaint-filter */", 19) == 0)
2153       info->mode = PREDEF_FILTERS;
2154    if (strncasecmp(header, "/* Xpaint-procedure */", 22) == 0)
2155       info->mode = PREDEF_PROCEDURES;
2156 
2157    /* compile C script */
2158    sprintf(cmd, "cd /tmp\ngcc -fPIC -I%s/include -I/usr/include/X11 "
2159                 "-c %s.c -o %s.o\n"
2160                 "gcc -fPIC -shared -Wl,-soname,%s.so %s.o -o %s.so\n",
2161 	        GetShareDir(),
2162                 tmp, tmp, strstr(tmp, "XPaint"), tmp, tmp);
2163    XtVaSetValues(info->cclog, XtNstring, cmd, NULL);
2164    pipe_command(cmd, NULL, CollectOutput, CollectOutput);
2165 
2166    /* clean *.c and *.o file */
2167    sprintf(cmd, "%s.c", tmp);
2168    unlink(cmd);
2169    sprintf(cmd, "%s.o", tmp);
2170    unlink(cmd);
2171 
2172    /* load dll library */
2173    sprintf(cmd, "%s.so", tmp);
2174 
2175    if (info->mode >= 0)
2176       dl_handle = dlopen(cmd, RTLD_LAZY);
2177 
2178    /* clean dll .so file */
2179    unlink(cmd);
2180    removeTempFile();
2181 
2182    if (dl_handle)
2183       sprintf(cmd, "echo \"%s\"",
2184 	      msgText[C_SCRIPT_SUCCESSFULLY_COMPILED_AND_LINKED]);
2185    else
2186       sprintf(cmd, "echo \"\"; echo \"%s\"; echo \"%s\"",
2187               msgText[C_SCRIPT_COULD_NOT_BE_COMPILED_OR_LINKED], dlerror());
2188 
2189    pipe_command(cmd, NULL, CollectOutput, CollectOutput);
2190    if (!dl_handle) return;
2191 
2192    if (info->mode == PREDEF_FILTERS) {
2193       if (dl_filter) dlclose(dl_filter);
2194       dl_filter = dl_handle;
2195    }
2196 
2197    if (info->mode == PREDEF_IMAGES) {
2198       if (dl_image) dlclose(dl_image);
2199       dl_image = dl_handle;
2200       iproc = dlsym(dl_image, "ImageCreate");
2201       if ((error = dlerror()) != NULL || !iproc) {
2202 	 if (!error) error = msgText[UNABLE_TO_CREATE_IMAGE];
2203          Notice(Global.toplevel, error);
2204          return;
2205       }
2206       pix = None;
2207       image = iproc();
2208       if (image) {
2209 	 screen = XtScreen(Global.toplevel);
2210 	 visual = DefaultVisualOfScreen(screen);
2211 	 cmap = XCreateColormap(XtDisplay(Global.toplevel),
2212 		    RootWindowOfScreen(screen), visual, AllocNone);
2213          alpha = image->alpha;
2214          image->alpha =  NULL;
2215 	 ImageToPixmap(image, Global.toplevel, &pix, &cmap);
2216       }
2217       if (!pix) {
2218          Notice(Global.toplevel, msgText[UNABLE_TO_CREATE_IMAGE]);
2219 	 return;
2220       }
2221       if (alpha) {
2222          file_isSpecialImage = 1;
2223          file_transparent = 1;
2224       }
2225       paint = graphicCreate(makeGraphicShell(Global.toplevel), 0, 0, -1,
2226                             pix, cmap, alpha);
2227       SetAlphaMode((Widget)paint, -1);
2228       return;
2229    }
2230 
2231    if (info->mode == PREDEF_PROCEDURES) {
2232       if (dl_proc) dlclose(dl_proc);
2233       dl_proc = dl_handle;
2234       proc = dlsym(dl_proc, "PaintProcedure");
2235       if ((error = dlerror()) != NULL) {
2236          Notice(Global.toplevel, error);
2237          return;
2238       }
2239       if (proc) proc(Global.toplevel);
2240       return;
2241    }
2242 }
2243 
2244 static void
externCallback(Widget w,XtPointer wlArg,XtPointer junk)2245 externCallback(Widget w, XtPointer wlArg, XtPointer junk)
2246 {
2247    FILE *fd;
2248    LocalInfo *info = (LocalInfo *) wlArg;
2249    int l;
2250    char *tmp, *ptr, *text;
2251    char name[256];
2252    char buf[2048];
2253 
2254    /* write buffer to file */
2255    PopdownMenusGlobal();
2256    fd = openTempFile(&tmp);
2257    fclose(fd);
2258    sprintf(name, "%s.c", tmp);
2259    removeTempFile();
2260    fd = fopen(name, "w");
2261    if (!fd) {
2262       Notice(w, msgText[UNABLE_TO_OPEN_FILE], name);
2263       return;
2264    }
2265    XtVaGetValues(info->program, XtNstring, &text, NULL);
2266    if (text && *text) {
2267       ptr = text;
2268       while (*ptr) {
2269          fputc(*ptr, fd);
2270          ++ptr;
2271       }
2272    }
2273    fclose(fd);
2274 
2275    /* call external editor */
2276    sprintf(buf, "%s %s", EDITOR, name);
2277    system(buf);
2278 
2279    /* Now copy edited file into buffer */
2280    fd = fopen(name, "r");
2281    if (!fd) {
2282       Notice(w, msgText[UNABLE_TO_OPEN_FILE], name);
2283       return;
2284    }
2285 
2286    text = xmalloc(2);
2287    *text = '\0';
2288    l = 0;
2289    while (fgets(buf, 2040, fd)) {
2290       l += strlen(buf);
2291       text = realloc(text, l+2);
2292       strcat(text, buf);
2293    }
2294    fclose(fd);
2295    unlink(name);
2296    XtVaSetValues(info->program, XtNstring, text, NULL);
2297 }
2298 
2299 static void
loadFileCallbackOK(Widget w,XtPointer wlArg,char * file)2300 loadFileCallbackOK(Widget w, XtPointer wlArg, char *file)
2301 {
2302    FILE *fd;
2303    LocalInfo *info = (LocalInfo *) wlArg;
2304    static char *text;
2305    char buf[2048];
2306    int l;
2307 
2308    if (!file || !*file) return;
2309    fd = fopen(file, "r");
2310    if (!fd) {
2311       Notice(w, msgText[UNABLE_TO_OPEN_FILE], file);
2312       return;
2313    }
2314    info->scriptfile = file;
2315    XtVaSetValues(info->name, XtNlabel, file, NULL);
2316    text = xmalloc(2);
2317    *text = '\0';
2318    l = 0;
2319    while (fgets(buf, 2040, fd)) {
2320       l += strlen(buf);
2321       text = realloc(text, l+2);
2322       strcat(text, buf);
2323    }
2324    fclose(fd);
2325    XtVaSetValues(info->program, XtNstring, text, NULL);
2326 }
2327 
2328 static void
loadFileCallback(Widget w,XtPointer wlArg,XtPointer junk)2329 loadFileCallback(Widget w, XtPointer wlArg, XtPointer junk)
2330 {
2331    char buf[256];
2332    *buf = '\0';
2333    if (getcwd(buf, 256)) strcat(buf, "/");
2334    Global.explore = True;
2335    GetFileName(GetShell(w), BROWSER_SIMPLEREAD,
2336                buf, (XtCallbackProc) loadFileCallbackOK, wlArg);
2337    Global.explore = False;
2338 }
2339 
2340 static void
saveFileCallbackOK(Widget w,XtPointer wlArg,char * file)2341 saveFileCallbackOK(Widget w, XtPointer wlArg, char *file)
2342 {
2343    FILE *fd;
2344    LocalInfo *info = (LocalInfo *) wlArg;
2345    char *ptr, *text;
2346 
2347    if (!file || !*file) return;
2348    fd = fopen(file, "w");
2349    if (!fd) {
2350       Notice(w, msgText[UNABLE_TO_OPEN_FILE], file);
2351       return;
2352    }
2353    info->scriptfile = file;
2354    XtVaSetValues(info->name, XtNlabel, file, NULL);
2355    XtVaGetValues(info->program, XtNstring, &text, NULL);
2356    if (!text || !*text) {
2357       fclose(fd);
2358       return;
2359    }
2360    ptr = text;
2361    while (*ptr) {
2362       fputc(*ptr, fd);
2363       ++ptr;
2364    }
2365    fclose(fd);
2366 }
2367 
2368 static void
saveFileCallback(Widget w,XtPointer wlArg,XtPointer junk)2369 saveFileCallback(Widget w, XtPointer wlArg, XtPointer junk)
2370 {
2371    LocalInfo *info = (LocalInfo *) wlArg;
2372    char buf[256];
2373    *buf = '\0';
2374    if (getcwd(buf, 256)) strcat(buf, "/");
2375    Global.explore = True;
2376    if (!info->scriptfile || !info->scriptfile[0])
2377       GetFileName(GetShell(w), BROWSER_SIMPLESAVE,
2378                buf, (XtCallbackProc) saveFileCallbackOK, wlArg);
2379    else
2380       saveFileCallbackOK(w, wlArg, info->scriptfile);
2381    Global.explore = False;
2382 }
2383 
2384 static void
saveasFileCallback(Widget w,XtPointer wlArg,XtPointer junk)2385 saveasFileCallback(Widget w, XtPointer wlArg, XtPointer junk)
2386 {
2387    char buf[256];
2388    *buf = '\0';
2389    if (getcwd(buf, 256)) strcat(buf, "/");
2390    Global.explore = True;
2391    GetFileName(GetShell(w), BROWSER_SIMPLESAVE,
2392                buf, (XtCallbackProc) saveFileCallbackOK, wlArg);
2393    Global.explore = False;
2394 }
2395 
2396 static void
loadScriptCallback(Widget w,XtPointer wlArg,XtPointer junk)2397 loadScriptCallback(Widget w, XtPointer wlArg, XtPointer junk)
2398 {
2399    LocalInfo *info = (LocalInfo *) wlArg;
2400    static char directory[256];
2401    int i;
2402 
2403    info->mode = PREDEF_IMAGES;
2404    for (i=0; i<PREDEF_SEPARATOR; i++)
2405       if (w == predefMenu[i].widget) {
2406 	 info->mode = i;
2407 	 break;
2408       }
2409 
2410    Global.explore = True;
2411    sprintf(directory, "%s/c_scripts/%s/", GetShareDir(),
2412 	              predefMenu[info->mode].name);
2413 
2414    GetFileName(GetShell(w), BROWSER_SIMPLEREAD, directory,
2415       (XtCallbackProc) loadFileCallbackOK, wlArg);
2416    Global.explore = False;
2417 }
2418 
2419 void
editorResized(Widget w,LocalInfo * l,XConfigureEvent * event,Boolean * flg)2420 editorResized(Widget w, LocalInfo * l, XConfigureEvent * event, Boolean * flg)
2421 {
2422     Dimension width, height;
2423     Dimension width1, height1, height2;
2424 
2425     XtVaGetValues(l->shell, XtNwidth, &width, XtNheight, &height, NULL);
2426     XtResizeWidget(XtParent(l->name), width, 38, BORDERWIDTH);
2427     XtResizeWidget(l->pane, width, height, BORDERWIDTH);
2428     XtVaSetValues(l->pane, XtNwidth, width, XtNheight, height, NULL);
2429     XtVaGetValues(XtParent(l->program), XtNheight, &height1, NULL);
2430 
2431 #ifdef XAW3DG
2432     width1 = (width>12)? width-10 : 2;
2433     height1 = (height1>10)? height1-8 : 2;
2434     height2 = (height>height1+63)? height - height1 - 53 : 10;
2435 #else
2436     width1 = (width>10)? width-8 : 2;
2437     height1 = (height1>10)? height1-8 : 2;
2438     height2 = (height>height1+60)? height - height1 - 50 : 10;
2439 #endif
2440     XtResizeWidget(XtParent(l->program), width1+8, height1+8, BORDERWIDTH);
2441     XtResizeWidget(l->program, width1, height1, BORDERWIDTH);
2442     XtVaSetValues(XtParent(l->program),
2443        XtNwidth, width1+8, XtNheight, height1+8, NULL);
2444     XtVaSetValues(l->program,
2445        XtNwidth, width1, XtNheight, height1, NULL);
2446     XtMoveWidget(XtParent(l->program), 0, 39);
2447     XtMoveWidget(l->program, 4, 0);
2448 
2449     XtResizeWidget(XtParent(l->cclog), width1+8, height2+8, BORDERWIDTH);
2450     XtResizeWidget(l->cclog, width1, height2, BORDERWIDTH);
2451     XtVaSetValues(XtParent(l->cclog),
2452        XtNwidth, width1+8, XtNheight, height2+8, NULL);
2453     XtVaSetValues(l->cclog,
2454        XtNwidth, width1, XtNheight, height2, NULL);
2455     XtMoveWidget(XtParent(l->cclog), 0, 46+height1);
2456     XtMoveWidget(l->cclog, 4, 0);
2457 }
2458 
2459 static void
popupHandler(Widget w,LocalInfo * info,XEvent * event,Boolean * flag)2460 popupHandler(Widget w, LocalInfo * info, XEvent * event, Boolean * flag)
2461 {
2462     if (Global.popped_up) {
2463         if (event->type == ButtonRelease)
2464             PopdownMenusGlobal();
2465         event->type = None;
2466     }
2467 }
2468 
2469 void *
ScriptEditor(Widget w,Widget paint)2470 ScriptEditor(Widget w, Widget paint)
2471 {
2472     Display *dpy;
2473     Window root;
2474     Widget topform, midform, botform;
2475     Widget bar, cancel, compile;
2476     LocalInfo *info = XtNew(LocalInfo);
2477     Arg args[4];
2478     int nargs = 0;
2479     int i;
2480 
2481     PopdownMenusGlobal();
2482 
2483     head = info;
2484     info->mode = 0;
2485     info->scriptfile = NULL;
2486 
2487     dpy = XtDisplay(GetShell(w));
2488     root = RootWindowOfScreen(XtScreen(GetShell(w)));
2489 
2490     info->shell = XtVisCreatePopupShell("XPaint / C scripts",
2491                                   topLevelShellWidgetClass,
2492 				  Global.toplevel, args, nargs);
2493     info->pane = XtVaCreateManagedWidget("pane", panedWidgetClass, info->shell,
2494                                    NULL);
2495     topform = XtVaCreateManagedWidget("topform", formWidgetClass, info->pane,
2496                                       NULL);
2497 
2498     bar = MenuBarCreate(topform, XtNumber(printMenuBar), printMenuBar);
2499     XtVaSetValues(bar, XtNvertDistance, 2, NULL);
2500 
2501     compile = XtVaCreateManagedWidget("compile",
2502 		 		    commandWidgetClass, topform,
2503 				    XtNfromHoriz, bar,
2504 				    XtNhorizDistance, 32,
2505 				    XtNvertDistance, 5,
2506 				    NULL);
2507     cancel = XtVaCreateManagedWidget("close",
2508 		 		    commandWidgetClass, topform,
2509 				    XtNfromHoriz, compile,
2510 				    XtNhorizDistance, 4,
2511 				    XtNvertDistance, 5,
2512 				    NULL);
2513 
2514     info->name = XtVaCreateManagedWidget("name",
2515 				    labelWidgetClass, topform,
2516 				    XtNlabel, "",
2517 				    XtNwidth, 350,
2518 				    XtNfromHoriz, cancel,
2519                                     XtNhorizDistance, 24,
2520                                     XtNvertDistance, 6,
2521 				    XtNborderWidth, 0,
2522                                     NULL);
2523 
2524     midform = XtVaCreateManagedWidget("midform",
2525                                     formWidgetClass, info->pane, NULL);
2526     botform = XtVaCreateManagedWidget("botform",
2527                                     formWidgetClass, info->pane, NULL);
2528 
2529     info->program =
2530           XtVaCreateManagedWidget("program", asciiTextWidgetClass, midform,
2531                                   XtNeditType, XawtextEdit,
2532                                   XtNresizable, True,
2533                                   XtNwidth, 620,
2534                                   XtNheight, 300,
2535 				  XtNstring, NULL,
2536 				  XtNscrollHorizontal, XawtextScrollWhenNeeded,
2537 				  XtNscrollVertical, XawtextScrollWhenNeeded,
2538 				  NULL);
2539     XtInsertRawEventHandler(info->program,
2540 			    ButtonPressMask | ButtonReleaseMask,
2541                             False, (XtEventHandler) popupHandler, info,
2542                             XtListHead);
2543 
2544 
2545     info->cclog =
2546           XtVaCreateManagedWidget("cclog", asciiTextWidgetClass, botform,
2547                                   XtNeditType, XawtextRead,
2548                                   XtNresizable, True,
2549                                   XtNwidth, 620,
2550                                   XtNheight, 100,
2551 				  XtNstring, NULL,
2552 				  XtNscrollHorizontal, XawtextScrollWhenNeeded,
2553 				  XtNscrollVertical, XawtextScrollWhenNeeded,
2554 				  NULL);
2555     XtInsertRawEventHandler(info->cclog,
2556 			    ButtonPressMask | ButtonReleaseMask,
2557                             False, (XtEventHandler) popupHandler, info,
2558                             XtListHead);
2559 
2560 
2561     AddDestroyCallback(info->shell,
2562                        (DestroyCallbackFunc) closeCallback, (XtPointer) info);
2563 
2564     XtAddCallback(fileMenu[FILE_LOAD].widget, XtNcallback,
2565 		     (XtCallbackProc) loadFileCallback, (XtPointer) info);
2566     XtAddCallback(fileMenu[FILE_SAVE].widget, XtNcallback,
2567 		     (XtCallbackProc) saveFileCallback, (XtPointer) info);
2568     XtAddCallback(fileMenu[FILE_SAVEAS].widget, XtNcallback,
2569 		     (XtCallbackProc) saveasFileCallback, (XtPointer) info);
2570     XtAddCallback(fileMenu[FILE_EDIT].widget, XtNcallback,
2571 		     (XtCallbackProc) externCallback, (XtPointer) info);
2572     XtAddCallback(fileMenu[FILE_CLOSE].widget, XtNcallback,
2573 		     (XtCallbackProc) closeCallback, (XtPointer) info);
2574     XtAddCallback(compile, XtNcallback,
2575                   (XtCallbackProc)compileCallback, (XtPointer) info);
2576     XtAddCallback(cancel, XtNcallback,
2577                   (XtCallbackProc)closeCallback, (XtPointer) info);
2578 
2579     for (i=0; i<PREDEF_SEPARATOR; i++)
2580        XtAddCallback(predefMenu[i].widget, XtNcallback,
2581 		     (XtCallbackProc) loadScriptCallback, (XtPointer) info);
2582 
2583     /* XtRealizeWidget(info->shell); */
2584     XtPopup(info->shell, XtGrabNone);
2585     SetWMInputHint(dpy, XtWindow(info->shell));
2586     XtVaSetValues(topform, XtNshowGrip, False, NULL);
2587     XtVaSetValues(midform, XtNshowGrip, True, NULL);
2588     XtVaSetValues(botform, XtNshowGrip, True, NULL);
2589 
2590     XtUnmanageChild(info->name);
2591     XMapWindow(dpy, XtWindow(info->name));
2592     XtUnmanageChild(cancel);
2593     XMapWindow(dpy, XtWindow(cancel));
2594     XtUnmanageChild(compile);
2595     XMapWindow(dpy, XtWindow(compile));
2596     XtUnmanageChild(info->program);
2597     XMapWindow(dpy, XtWindow(info->program));
2598     XtUnmanageChild(info->cclog);
2599     XMapWindow(dpy, XtWindow(info->cclog));
2600 
2601     XtAddEventHandler(midform, StructureNotifyMask, False,
2602 		      (XtEventHandler) editorResized, (XtPointer) info);
2603     XtAddEventHandler(info->shell, StructureNotifyMask, False,
2604 		      (XtEventHandler) editorResized, (XtPointer) info);
2605     XtAddEventHandler(info->program, ButtonPressMask, False,
2606 		      (XtEventHandler) mousewheelScroll, (XtPointer) NULL);
2607     XtAddEventHandler(info->cclog, ButtonPressMask, False,
2608 		      (XtEventHandler) mousewheelScroll, (XtPointer) NULL);
2609     XtSetMinSizeHints(info->shell, 300, 160);
2610     return info;
2611 }
2612 
2613 /* convenience routines */
2614 int
RegionX()2615 RegionX()
2616 {
2617     PaintWidget pw = (PaintWidget)Global.curpaint;
2618     return pw->paint.region.rect.x;
2619 }
2620 int
RegionY()2621 RegionY()
2622 {
2623     PaintWidget pw = (PaintWidget)Global.curpaint;
2624     return pw->paint.region.rect.y;
2625 }
2626 int
RegionWidth()2627 RegionWidth()
2628 {
2629     PaintWidget pw = (PaintWidget)Global.curpaint;
2630     return pw->paint.region.rect.width;
2631 }
2632 int
RegionHeight()2633 RegionHeight()
2634 {
2635     PaintWidget pw = (PaintWidget)Global.curpaint;
2636     return pw->paint.region.rect.height;
2637 }
2638 
ImageToRegion(Image * input,XRectangle rect,Pixmap mask)2639 void ImageToRegion(Image * input, XRectangle rect, Pixmap mask)
2640 {
2641     Pixmap pix;
2642     Colormap cmap;
2643     ImageToPixmap(input, Global.curpaint, &pix, &cmap);
2644     PwRegionSet(Global.curpaint, &rect, pix, mask);
2645 }
2646 
ImageToCanvas(Image * input,Pixmap mask)2647 void ImageToCanvas(Image * input, Pixmap mask)
2648 {
2649     Pixmap pix;
2650     Colormap cmap;
2651     XRectangle rect;
2652     if (!input || !Global.curpaint) return;
2653     rect.width = input->width;
2654     rect.height = input->height;
2655     rect.x = 0;
2656     rect.y = 0;
2657     ImageToPixmap(input, Global.curpaint, &pix, &cmap);
2658     PwRegionSet(Global.curpaint, &rect, pix, mask);
2659     PwRegionSet(Global.curpaint, None, None, None);
2660 }
2661 
2662 Image *
CanvasToImage()2663 CanvasToImage()
2664 {
2665     PaintWidget pw = (PaintWidget)Global.curpaint;
2666     Image * image;
2667 
2668     image = PixmapToImage((Widget)pw, GET_PIXMAP(pw), Global.clipboard.cmap);
2669     return image;
2670 }
2671 
2672 Boolean
isFilterDefined()2673 isFilterDefined()
2674 {
2675     return (dl_filter)? True : False;
2676 }
2677 
2678 Image *
ImageUserDefined(Image * input)2679 ImageUserDefined(Image * input)
2680 {
2681     Image *output;
2682     char * error;
2683     void (* proc)(Image *, Image *);
2684 
2685     if (!dl_filter) return NULL;
2686 
2687     proc = dlsym(dl_filter, "FilterProcess");
2688     if ((error = dlerror()) != NULL) {
2689        Notice(Global.toplevel, error);
2690        return input;
2691     }
2692 
2693     output = ImageNew(input->width, input->height);
2694 
2695     proc(input, output);
2696 
2697     return output;
2698 }
2699 
2700 
2701 #define GRAY(p)		(((p)[0]*169 + (p)[1]*256 + (p)[2]*87)/512)
2702 #define SQ(x)		((x)*(x))
2703 
2704 /*
2705  * Directional filter, according to the algorithm described on p. 60 of:
2706  * _Algorithms_for_Graphics_and_Image_Processing_. Theo Pavlidis.
2707  * Computer Science Press, 1982.
2708  * For each pixel, detect the most prominent edge and apply a filter that
2709  * does not degrade that edge.
2710  */
2711 Image *
ImageDirectionalFilter(Image * input)2712 ImageDirectionalFilter(Image * input)
2713 {
2714     unsigned char *p00, *p10, *p_0, *p11, *p__, *p01, *p0_, *p_1, *p1_;
2715     int g00, g10, g_0, g11, g__, g01, g0_, g_1, g1_;
2716     int v0, v45, v90, v135, vmin, theta;
2717     int x, y;
2718     Image *output = ImageNew(input->width, input->height);
2719     unsigned char *op = output->data;
2720 
2721     /*
2722      * We don't process the border of the image tto avoid the hassle
2723      * of having do deal with boundary conditions. Hopefully no one
2724      * will notice.
2725      */
2726     /* copy first row unchanged */
2727     for (x = 0; x < input->width; x++) {
2728 	p00 = ImagePixel(input, x, 0);
2729 	*op++ = *p00++;
2730 	*op++ = *p00++;
2731 	*op++ = *p00++;
2732     }
2733 
2734     for (y = 1; y < input->height - 1; y++) {
2735 	/* copy first column unchanged */
2736 	p00 = ImagePixel(input, 0, y);
2737 	*op++ = *p00++;
2738 	*op++ = *p00++;
2739 	*op++ = *p00++;
2740 	for (x = 1; x < input->width - 1; x++) {
2741 	    /* find values of pixel and all neighbours */
2742 	    p00 = ImagePixel(input, x, y);
2743 	    p10 = ImagePixel(input, x + 1, y);
2744 	    p_0 = ImagePixel(input, x - 1, y);
2745 	    p11 = ImagePixel(input, x + 1, y + 1);
2746 	    p__ = ImagePixel(input, x - 1, y - 1);
2747 	    p01 = ImagePixel(input, x, y + 1);
2748 	    p0_ = ImagePixel(input, x, y - 1);
2749 	    p_1 = ImagePixel(input, x - 1, y + 1);
2750 	    p1_ = ImagePixel(input, x + 1, y - 1);
2751 
2752 	    /* get grayscale values */
2753 	    g00 = GRAY(p00);
2754 	    g01 = GRAY(p01);
2755 	    g10 = GRAY(p10);
2756 	    g11 = GRAY(p11);
2757 	    g0_ = GRAY(p0_);
2758 	    g_0 = GRAY(p_0);
2759 	    g__ = GRAY(p__);
2760 	    g_1 = GRAY(p_1);
2761 	    g1_ = GRAY(p1_);
2762 
2763 	    /* estimate direction of edge, if any */
2764 	    v0 = SQ(g00 - g10) + SQ(g00 - g_0);
2765 	    v45 = SQ(g00 - g11) + SQ(g00 - g__);
2766 	    v90 = SQ(g00 - g01) + SQ(g00 - g0_);
2767 	    v135 = SQ(g00 - g_1) + SQ(g00 - g1_);
2768 
2769 	    vmin = MIN(MIN(v0, v45), MIN(v90, v135));
2770 	    theta = 0;
2771 	    if (vmin == v45)
2772 		theta = 1;
2773 	    else if (vmin == v90)
2774 		theta = 2;
2775 	    else if (vmin == v135)
2776 		theta = 3;
2777 
2778 	    /* apply filtering according to direction of edge */
2779 	    switch (theta) {
2780 	    case 0:		/* 0 degrees */
2781 		*op++ = (*p_0++ + *p00++ + *p10++) / 3;
2782 		*op++ = (*p_0++ + *p00++ + *p10++) / 3;
2783 		*op++ = (*p_0++ + *p00++ + *p10++) / 3;
2784 		break;
2785 	    case 1:		/* 45 degrees */
2786 		*op++ = (*p__++ + *p00++ + *p11++) / 3;
2787 		*op++ = (*p__++ + *p00++ + *p11++) / 3;
2788 		*op++ = (*p__++ + *p00++ + *p11++) / 3;
2789 		break;
2790 	    case 2:		/* 90 degrees */
2791 		*op++ = (*p0_++ + *p00++ + *p01++) / 3;
2792 		*op++ = (*p0_++ + *p00++ + *p01++) / 3;
2793 		*op++ = (*p0_++ + *p00++ + *p01++) / 3;
2794 		break;
2795 	    case 3:		/* 135 degrees */
2796 		*op++ = (*p1_++ + *p00++ + *p_1++) / 3;
2797 		*op++ = (*p1_++ + *p00++ + *p_1++) / 3;
2798 		*op++ = (*p1_++ + *p00++ + *p_1++) / 3;
2799 		break;
2800 	    }
2801 	}
2802 	/* copy last column unchanged */
2803 	p00 = ImagePixel(input, x, y);
2804 	*op++ = *p00++;
2805 	*op++ = *p00++;
2806 	*op++ = *p00++;
2807     }
2808 
2809     /* copy last row unchanged */
2810     for (x = 0; x < input->width; x++) {
2811 	p00 = ImagePixel(input, x, y);
2812 	*op++ = *p00++;
2813 	*op++ = *p00++;
2814 	*op++ = *p00++;
2815     }
2816     return output;
2817 }
2818 
2819 #if 0
2820 #define ISFOREGND(p) ((p) ? \
2821 		      (deltaR-deltaRV <= p[0]) && (p[0] <= deltaR+deltaRV) && \
2822 		      (deltaG-deltaGV <= p[1]) && (p[1] <= deltaG+deltaGV) && \
2823 		      (deltaB-deltaBV <= p[2]) && (p[2] <= deltaB+deltaBV) : 1)
2824 
2825 /*
2826  * Thicken an image.
2827  */
2828 Image *
2829 ImageThicken(Image * input)
2830 {
2831     unsigned char *p00, *p10, *p_0, *p01, *p0_;
2832     int x, y, width, height, br, bg, bb;
2833     int deltaR, deltaRV, deltaG, deltaGV, deltaB, deltaBV;
2834     Image *output;
2835     unsigned char *op;
2836 
2837     width = input->width;
2838     height = input->height;
2839     output = ImageNew(width, height);
2840     op = output->data;
2841 
2842     /* Get RGB values of background colour */
2843     br = ImgProcessInfo.background->red / 256;
2844     bg = ImgProcessInfo.background->green / 256;
2845     bb = ImgProcessInfo.background->blue / 256;
2846 
2847     for (y = 0; y < height; y++)
2848 	for (x = 0; x < width; x++) {
2849 	    /* find values of pixel and all d-neighbours */
2850 	    p00 = ImagePixel(input, x, y);
2851 	    p10 = x < width - 1 ? ImagePixel(input, x + 1, y) : NULL;
2852 	    p_0 = x > 0 ? ImagePixel(input, x - 1, y) : NULL;
2853 	    p01 = y < height - 1 ? ImagePixel(input, x, y + 1) : NULL;
2854 	    p0_ = y > 0 ? ImagePixel(input, x, y - 1) : NULL;
2855 
2856 	    if (ISFOREGND(p00)) {
2857 		*op++ = *p00++;
2858 		*op++ = *p00++;
2859 		*op++ = *p00++;
2860 	    } else {
2861 		*op++ = br;
2862 		*op++ = bg;
2863 		*op++ = bb;
2864 	    }
2865 	}
2866 
2867     return output;
2868 }
2869 #endif
2870