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