ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/px/x11image.c
Revision: 2.80
Committed: Tue Jun 3 21:31:51 2025 UTC (2 days, 18 hours ago) by greg
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 2.79: +2 -4 lines
Log Message:
refactor: More consistent use of global char * progname and fixargv0()

File Contents

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: x11image.c,v 2.79 2025/04/28 01:37:07 greg Exp $";
3 #endif
4 /*
5 * x11image.c - driver for X-windows
6 *
7 * 3/1/90
8 */
9
10 /*
11 * Modified for X11
12 *
13 * January 1990
14 *
15 * Anat Grynberg and Greg Ward
16 */
17
18
19 #include "standard.h"
20
21 #include <string.h>
22 #include <signal.h>
23 #include <unistd.h>
24 #include <sys/types.h>
25 #include <sys/wait.h>
26 #include <X11/Xlib.h>
27 #include <X11/cursorfont.h>
28 #include <X11/Xutil.h>
29 #include <X11/Xatom.h>
30
31 #include "color.h"
32 #include "tonemap.h"
33 #include "clrtab.h"
34 #include "view.h"
35 #include "x11raster.h"
36 #include "random.h"
37
38 #define FONTNAME "8x13" /* text font we'll use */
39
40 #define CTRL(c) ((c)-'@')
41
42 #define BORWIDTH 5 /* border width */
43
44 #define ICONSIZ (8*10) /* maximum icon dimension (even 8) */
45
46 #define FIXWEIGHT 20 /* weight to add for fixation points */
47
48 #define ourscreen DefaultScreen(thedisplay)
49 #define ourroot RootWindow(thedisplay,ourscreen)
50
51 #define revline(x0,y0,x1,y1) XDrawLine(thedisplay,wind,revgc,x0,y0,x1,y1)
52
53 #define redraw(x,y,w,h) patch_raster(wind,(x)-xoff,(y)-yoff,x,y,w,h,ourras)
54
55 double gamcor = 2.2; /* gamma correction */
56 char *gamstr = NULL; /* gamma value override */
57
58 int dither = 1; /* dither colors? */
59 int fast = 0; /* keep picture in Pixmap? */
60
61 char *dispname = NULL; /* our display name */
62
63 Window wind = 0; /* our output window */
64 unsigned long ourblack=0, ourwhite=1; /* black and white for this visual */
65 int maxcolors = 0; /* maximum colors */
66 int greyscale = 0; /* in grey */
67
68 int scale = 0; /* scalefactor; power of two */
69
70 int xoff = 0; /* x image offset */
71 int yoff = 0; /* y image offset */
72
73 int parent = 0; /* number of children, -1 if child */
74 int sequential = 0; /* display images in sequence */
75
76 char *tout = "od"; /* output of 't' command */
77 int tinterv = 0; /* interval between mouse reports */
78
79 int tmflags = TM_F_LINEAR; /* tone mapping flags */
80
81 VIEW ourview = STDVIEW; /* image view parameters */
82 int gotview = 0; /* got parameters from file */
83
84 COLR *scanline; /* scan line buffer */
85 TMbright *lscan; /* encoded luminance scanline */
86 uby8 *cscan; /* encoded chroma scanline */
87 uby8 *pscan; /* compute pixel scanline */
88
89 RESOLU inpres; /* input resolution and ordering */
90 int xmax, ymax; /* picture dimensions */
91 int width, height; /* window size */
92 char *fname = NULL; /* input file name */
93 FILE *fin = NULL; /* input file */
94 long *scanpos = NULL; /* scan line positions in file */
95 int cury = 0; /* current scan location */
96
97 double exposure = 1.0; /* exposure compensation used */
98
99 int wrongformat = 0; /* input in another format? */
100
101 TMstruct *tmGlobal; /* base tone-mapping */
102 TMstruct *tmCurrent; /* curren tone-mapping */
103
104 GC ourgc; /* standard graphics context */
105 GC revgc; /* graphics context with GXinvert */
106
107 int *ourrank; /* our visual class ranking */
108 XVisualInfo ourvis; /* our visual */
109 XRASTER *ourras; /* our stored image */
110 unsigned char *ourdata; /* our image data */
111
112 struct {
113 int xmin, ymin, xsiz, ysiz;
114 } bbox = {0, 0, 0, 0}; /* current bbox */
115
116 char *geometry = NULL; /* geometry specification */
117
118 char icondata[ICONSIZ*ICONSIZ/8]; /* icon bitmap data */
119 int iconwidth = 0, iconheight = 0;
120
121 char errmsg[128];
122
123 uby8 clrtab[256][3]; /* global color map */
124
125
126 Display *thedisplay;
127 Atom closedownAtom, wmProtocolsAtom;
128
129 int sigrecv;
130
131 void onsig(int i) { sigrecv++; }
132
133 typedef void doboxf_t(COLR *scn, int n, void *);
134
135 static gethfunc headline;
136 static void init(int argc, char **argv);
137 static void quiterr(char *err);
138 static int viscmp(XVisualInfo *v1, XVisualInfo *v2);
139 static void getbestvis(void);
140 static void getras(void);
141 static void getevent(void);
142 static int traceray(int xpos, int ypos);
143 static int docom(XKeyPressedEvent *ekey);
144 static void moveimage(XButtonPressedEvent *ebut);
145 static void getbox(XButtonPressedEvent *ebut);
146 static void trackrays(XButtonPressedEvent *ebut);
147 static void revbox(int x0, int y0, int x1, int y1);
148 static doboxf_t colavg;
149 static doboxf_t addfix;
150 static int avgbox(COLOR cavg);
151 static int dobox(doboxf_t *f, void *p);
152 static void make_tonemap(void);
153 static void tmap_colrs(COLR *scn, int len);
154 static void getmono(void);
155 static void add2icon(int y, COLR *scan);
156 static void getfull(void);
157 static void getgrey(void);
158 static void getmapped(void);
159 static void scale_rcolors(XRASTER *xr, double sf);
160 static int getscan(int y);
161
162
163 int
164 main(int argc, char *argv[])
165 {
166 int i;
167 int pid;
168
169 fixargv0(argv[0]); /* sets global progname */
170 fin = stdin;
171
172 for (i = 1; i < argc; i++)
173 if (argv[i][0] == '-')
174 switch (argv[i][1]) {
175 case 'c': /* number of colors */
176 maxcolors = atoi(argv[++i]);
177 break;
178 case 'b': /* greyscale only */
179 greyscale = !greyscale;
180 break;
181 case 'm': /* monochrome */
182 greyscale = 1;
183 maxcolors = 2;
184 break;
185 case 'd': /* display or dither */
186 if (argv[i][2] == 'i')
187 dispname = argv[++i];
188 else
189 dither = !dither;
190 break;
191 case 'f': /* save pixmap */
192 fast = !fast;
193 break;
194 case 's': /* one at a time */
195 sequential = !sequential;
196 break;
197 case 'o': /* 't' output */
198 tout = argv[i]+2;
199 break;
200 case 't': /* msec interval */
201 tinterv = atoi(argv[++i]);
202 break;
203 case 'e': /* exposure comp. */
204 i++;
205 if (argv[i][0] == 'a') {
206 tmflags = TM_F_CAMERA;
207 break;
208 }
209 if (argv[i][0] == 'h') {
210 tmflags = TM_F_HUMAN;
211 break;
212 }
213 if (argv[i][0] != '+' && argv[i][0] != '-')
214 goto userr;
215 scale = atoi(argv[i]);
216 break;
217 case 'g': /* gamma comp. */
218 if (argv[i][2] == 'e')
219 geometry = argv[++i];
220 else
221 gamstr = argv[++i];
222 break;
223 default:
224 goto userr;
225 }
226 else if (argv[i][0] == '=')
227 geometry = argv[i];
228 else
229 break;
230
231 if (i > argc)
232 goto userr;
233 while (i < argc-1) {
234 sigrecv = 0;
235 signal(SIGCONT, onsig);
236 if ((pid=fork()) == 0) { /* a child for each picture */
237 parent = -1;
238 break;
239 }
240 if (pid < 0)
241 quiterr("fork failed");
242 parent++;
243 while (!sigrecv)
244 pause(); /* wait for wake-up call */
245 i++;
246 }
247 if (i < argc) { /* open picture file */
248 fname = argv[i];
249 fin = fopen(fname, "r");
250 if (fin == NULL)
251 quiterr("cannot open picture file");
252 }
253 /* get header */
254 getheader(fin, headline, NULL);
255 /* get picture dimensions */
256 if (wrongformat || !fgetsresolu(&inpres, fin))
257 quiterr("bad picture format");
258 xmax = scanlen(&inpres);
259 ymax = numscans(&inpres);
260 /* set view parameters */
261 if (gotview && setview(&ourview) != NULL)
262 gotview = 0;
263 if ((scanline = (COLR *)malloc(xmax*sizeof(COLR))) == NULL)
264 quiterr("out of memory");
265
266 init(argc, argv); /* get file and open window */
267
268 for ( ; ; )
269 getevent(); /* main loop */
270 userr:
271 fprintf(stderr,
272 "Usage: %s [-di disp][[-ge] spec][-b][-m][-d][-f][-c nclrs][-e spec][-g gamcor][-s][-ospec][-t intvl] hdr ..\n",
273 progname);
274 exit(1);
275 }
276
277
278 static int
279 headline( /* get relevant info from header */
280 char *s,
281 void *p
282 )
283 {
284 char fmt[MAXFMTLEN];
285
286 if (isexpos(s))
287 exposure *= exposval(s);
288 else if (formatval(fmt, s))
289 wrongformat = strcmp(fmt, COLRFMT) && strcmp(fmt, SPECFMT);
290 else if (isview(s))
291 gotview += sscanview(&ourview, s);
292 else if (isncomp(s))
293 NCSAMP = ncompval(s);
294 else if (iswlsplit(s))
295 wlsplitval(WLPART, s);
296 return(0);
297 }
298
299
300 static void
301 init( /* get data and open window */
302 int argc,
303 char **argv
304 )
305 {
306 XSetWindowAttributes ourwinattr;
307 XClassHint xclshints;
308 XWMHints xwmhints;
309 XSizeHints xszhints;
310 XTextProperty windowName, iconName;
311 XGCValues xgcv;
312 char *name;
313 int i;
314
315 if (fname != NULL) {
316 scanpos = (long *)malloc(ymax*sizeof(long));
317 if (scanpos == NULL)
318 quiterr("out of memory");
319 for (i = 0; i < ymax; i++)
320 scanpos[i] = -1;
321 name = fname;
322 } else
323 name = progname;
324 /* remove directory prefix from name */
325 for (i = strlen(name); i-- > 0; )
326 if (name[i] == '/')
327 break;
328 name += i+1;
329 if ((thedisplay = XOpenDisplay(dispname)) == NULL)
330 quiterr("cannot open display");
331 /* set gamma value */
332 if (gamstr == NULL) /* get it from the X server */
333 gamstr = XGetDefault(thedisplay, "radiance", "gamma");
334 if (gamstr == NULL) /* get it from the environment */
335 gamstr = getenv("DISPLAY_GAMMA");
336 if (gamstr != NULL)
337 gamcor = atof(gamstr);
338 /* get best visual for default screen */
339 getbestvis();
340 /* store image */
341 getras();
342 /* get size and position */
343 xszhints.flags = 0;
344 xszhints.width = xmax; xszhints.height = ymax;
345 if (geometry != NULL) {
346 i = XParseGeometry(geometry, &xszhints.x, &xszhints.y,
347 (unsigned *)&xszhints.width,
348 (unsigned *)&xszhints.height);
349 if ((i&(WidthValue|HeightValue)) == (WidthValue|HeightValue))
350 xszhints.flags |= USSize;
351 else
352 xszhints.flags |= PSize;
353 if ((i&(XValue|YValue)) == (XValue|YValue)) {
354 xszhints.flags |= USPosition;
355 if (i & XNegative)
356 xszhints.x += DisplayWidth(thedisplay,
357 ourscreen)-1-xszhints.width-2*BORWIDTH;
358 if (i & YNegative)
359 xszhints.y += DisplayHeight(thedisplay,
360 ourscreen)-1-xszhints.height-2*BORWIDTH;
361 }
362 }
363 /* open window */
364 i = CWEventMask|CWCursor|CWBackPixel|CWBorderPixel;
365 ourwinattr.border_pixel = ourwhite;
366 ourwinattr.background_pixel = ourblack;
367 if (ourvis.visual != DefaultVisual(thedisplay,ourscreen)) {
368 ourwinattr.colormap = newcmap(thedisplay, ourscreen, ourvis.visual);
369 i |= CWColormap;
370 }
371 ourwinattr.event_mask = ExposureMask|KeyPressMask|ButtonPressMask|
372 ButtonReleaseMask|ButtonMotionMask|StructureNotifyMask;
373 ourwinattr.cursor = XCreateFontCursor(thedisplay, XC_diamond_cross);
374 wind = XCreateWindow(thedisplay, ourroot, xszhints.x, xszhints.y,
375 xszhints.width, xszhints.height, BORWIDTH,
376 ourvis.depth, InputOutput, ourvis.visual,
377 i, &ourwinattr);
378 if (wind == 0)
379 quiterr("cannot create window");
380 width = xmax;
381 height = ymax;
382 /* prepare graphics drawing context */
383 if ((xgcv.font = XLoadFont(thedisplay, FONTNAME)) == 0)
384 quiterr("cannot get font");
385 xgcv.foreground = ourblack;
386 xgcv.background = ourwhite;
387 ourgc = XCreateGC(thedisplay, wind, GCForeground|GCBackground|
388 GCFont, &xgcv);
389 xgcv.function = GXinvert;
390 revgc = XCreateGC(thedisplay, wind, GCForeground|GCBackground|
391 GCFunction, &xgcv);
392
393 /* set up the window manager */
394 xwmhints.flags = InputHint|IconPixmapHint;
395 xwmhints.input = True;
396 xwmhints.icon_pixmap = XCreateBitmapFromData(thedisplay,
397 wind, icondata, iconwidth, iconheight);
398
399 windowName.encoding = iconName.encoding = XA_STRING;
400 windowName.format = iconName.format = 8;
401 windowName.value = (u_char *)name;
402 windowName.nitems = strlen((char *)windowName.value);
403 iconName.value = (u_char *)name;
404 iconName.nitems = strlen((char *)windowName.value);
405
406 xclshints.res_name = NULL;
407 xclshints.res_class = "Ximage";
408 XSetWMProperties(thedisplay, wind, &windowName, &iconName,
409 argv, argc, &xszhints, &xwmhints, &xclshints);
410 closedownAtom = XInternAtom(thedisplay, "WM_DELETE_WINDOW", False);
411 wmProtocolsAtom = XInternAtom(thedisplay, "WM_PROTOCOLS", False);
412 XSetWMProtocols(thedisplay, wind, &closedownAtom, 1);
413
414 XMapWindow(thedisplay, wind);
415 } /* end of init */
416
417
418 static void
419 quiterr( /* print message and exit */
420 char *err
421 )
422 {
423 int es;
424 int cs;
425
426 if ( (es = (err != NULL)) )
427 fprintf(stderr, "%s: %s: %s\n", progname,
428 fname==NULL?"<stdin>":fname, err);
429 if (thedisplay != NULL)
430 XCloseDisplay(thedisplay);
431 if ((parent < 0) & (sigrecv == 0))
432 kill(getppid(), SIGCONT);
433 while (parent > 0 && wait(&cs) != -1) { /* wait for any children */
434 if (es == 0)
435 es = cs>>8 & 0xff;
436 parent--;
437 }
438 exit(es);
439 }
440
441
442 static int
443 viscmp( /* compare visual to see which is better, descending */
444 XVisualInfo *v1,
445 XVisualInfo *v2
446 )
447 {
448 int bad1 = 0, bad2 = 0;
449 int *rp;
450
451 if (v1->class == v2->class) {
452 if ((v1->class == TrueColor) | (v1->class == DirectColor)) {
453 /* prefer 24-bit */
454 if ((v1->depth == 24) & (v2->depth > 24))
455 return(-1);
456 if ((v1->depth > 24) & (v2->depth == 24))
457 return(1);
458 /* go for maximum depth otherwise */
459 return(v2->depth - v1->depth);
460 }
461 /* don't be too greedy */
462 if ((maxcolors <= 1<<v1->depth) & (maxcolors <= 1<<v2->depth))
463 return(v1->depth - v2->depth);
464 return(v2->depth - v1->depth);
465 }
466 /* prefer Pseudo when < 15-bit */
467 if ((v1->class == TrueColor) | (v1->class == DirectColor) &&
468 v1->depth < 15)
469 bad1 = 1;
470 if ((v2->class == TrueColor) | (v2->class == DirectColor) &&
471 v2->depth < 15)
472 bad2 = -1;
473 if (bad1 | bad2)
474 return(bad1+bad2);
475 /* otherwise, use class ranking */
476 for (rp = ourrank; *rp != -1; rp++) {
477 if (v1->class == *rp)
478 return(-1);
479 if (v2->class == *rp)
480 return(1);
481 }
482 return(0);
483 }
484
485
486 static void
487 getbestvis(void) /* get the best visual for this screen */
488 {
489 #ifdef DEBUG
490 static char vistype[][12] = {
491 "StaticGray",
492 "GrayScale",
493 "StaticColor",
494 "PseudoColor",
495 "TrueColor",
496 "DirectColor"
497 };
498 #endif
499 static int rankings[3][6] = {
500 {TrueColor,DirectColor,PseudoColor,GrayScale,StaticGray,-1},
501 {PseudoColor,GrayScale,StaticGray,-1},
502 {PseudoColor,GrayScale,StaticGray,-1}
503 };
504 XVisualInfo *xvi;
505 int vismatched;
506 int i, j;
507
508 if (greyscale) {
509 ourrank = rankings[2];
510 if (maxcolors < 2) maxcolors = 256;
511 } else if (maxcolors >= 2 && maxcolors <= 256)
512 ourrank = rankings[1];
513 else {
514 ourrank = rankings[0];
515 maxcolors = 256;
516 }
517 /* find best visual */
518 ourvis.screen = ourscreen;
519 xvi = XGetVisualInfo(thedisplay,VisualScreenMask,&ourvis,&vismatched);
520 if (xvi == NULL)
521 quiterr("no visuals for this screen!");
522 #ifdef DEBUG
523 fprintf(stderr, "Supported visuals:\n");
524 for (i = 0; i < vismatched; i++)
525 fprintf(stderr, "\ttype %s, depth %d\n",
526 vistype[xvi[i].class], xvi[i].depth);
527 #endif
528 for (i = 0, j = 1; j < vismatched; j++)
529 if (viscmp(&xvi[i],&xvi[j]) > 0)
530 i = j;
531 /* compare to least acceptable */
532 for (j = 0; ourrank[j++] != -1; )
533 ;
534 ourvis.class = ourrank[--j];
535 ourvis.depth = 1;
536 if (viscmp(&xvi[i],&ourvis) > 0)
537 quiterr("inadequate visuals on this screen");
538 /* OK, we'll use it */
539 ourvis = xvi[i];
540 #ifdef DEBUG
541 fprintf(stderr, "Selected visual type %s, depth %d\n",
542 vistype[ourvis.class], ourvis.depth);
543 #endif
544 /* make appropriate adjustments */
545 if (ourvis.class == GrayScale || ourvis.class == StaticGray)
546 greyscale = 1;
547 if (ourvis.depth <= 8 && ourvis.colormap_size < maxcolors)
548 maxcolors = ourvis.colormap_size;
549 if (ourvis.class == StaticGray) {
550 ourblack = 0;
551 ourwhite = 255;
552 } else if (ourvis.class == PseudoColor) {
553 ourblack = BlackPixel(thedisplay,ourscreen);
554 ourwhite = WhitePixel(thedisplay,ourscreen);
555 if ((ourblack|ourwhite) & ~255L) {
556 ourblack = 0;
557 ourwhite = 1;
558 }
559 if (maxcolors > 4)
560 maxcolors -= 2;
561 } else {
562 ourblack = 0;
563 ourwhite = ourvis.red_mask|ourvis.green_mask|ourvis.blue_mask;
564 }
565 XFree((char *)xvi);
566 }
567
568
569 static void
570 getras(void) /* get raster file */
571 {
572 if (maxcolors <= 2) { /* monochrome */
573 ourdata = (unsigned char *)malloc(ymax*((xmax+7)/8));
574 if (ourdata == NULL)
575 goto fail;
576 ourras = make_raster(thedisplay, &ourvis, 1, (char *)ourdata,
577 xmax, ymax, 8);
578 if (ourras == NULL)
579 goto fail;
580 getmono();
581 } else if ((ourvis.class == TrueColor) | (ourvis.class == DirectColor)) {
582 int datsiz = ourvis.depth>16 ? sizeof(int32) : sizeof(int16);
583 ourdata = (unsigned char *)malloc(datsiz*xmax*ymax);
584 if (ourdata == NULL)
585 goto fail;
586 ourras = make_raster(thedisplay, &ourvis, datsiz*8,
587 (char *)ourdata, xmax, ymax, datsiz*8);
588 if (ourras == NULL)
589 goto fail;
590 getfull();
591 } else {
592 ourdata = (unsigned char *)malloc(xmax*ymax);
593 if (ourdata == NULL)
594 goto fail;
595 ourras = make_raster(thedisplay, &ourvis, 8, (char *)ourdata,
596 xmax, ymax, 8);
597 if (ourras == NULL)
598 goto fail;
599 if (greyscale)
600 getgrey();
601 else
602 getmapped();
603 if (ourvis.class != StaticGray && !init_rcolors(ourras,clrtab))
604 goto fail;
605 }
606 return;
607 fail:
608 quiterr("could not create raster image");
609 }
610
611
612 static void
613 getevent(void) /* process the next event */
614 {
615 XEvent xev;
616
617 XNextEvent(thedisplay, &xev);
618 switch ((int)xev.type) {
619 case KeyPress:
620 docom(&xev.xkey);
621 break;
622 case ConfigureNotify:
623 width = xev.xconfigure.width;
624 height = xev.xconfigure.height;
625 break;
626 case MapNotify:
627 map_rcolors(ourras, wind);
628 if (fast)
629 make_rpixmap(ourras, wind);
630 if ((!sequential) & (parent < 0) & (sigrecv == 0)) {
631 kill(getppid(), SIGCONT);
632 sigrecv--;
633 }
634 break;
635 case UnmapNotify:
636 if (!fast)
637 unmap_rcolors(ourras);
638 break;
639 case Expose:
640 redraw(xev.xexpose.x, xev.xexpose.y,
641 xev.xexpose.width, xev.xexpose.height);
642 break;
643 case ButtonPress:
644 if (xev.xbutton.state & (ShiftMask|ControlMask))
645 moveimage(&xev.xbutton);
646 else
647 switch (xev.xbutton.button) {
648 case Button1:
649 getbox(&xev.xbutton);
650 break;
651 case Button2:
652 traceray(xev.xbutton.x, xev.xbutton.y);
653 break;
654 case Button3:
655 trackrays(&xev.xbutton);
656 break;
657 }
658 break;
659 case ClientMessage:
660 if ((xev.xclient.message_type == wmProtocolsAtom) &&
661 (xev.xclient.data.l[0] == closedownAtom))
662 quiterr(NULL);
663 break;
664 }
665 }
666
667
668 static int
669 traceray( /* print requested pixel data */
670 int xpos,
671 int ypos
672 )
673 {
674 RREAL hv[2];
675 FVECT rorg, rdir;
676 COLOR cval;
677 char *cp;
678
679 bbox.xmin = xpos; bbox.xsiz = 1;
680 bbox.ymin = ypos; bbox.ysiz = 1;
681 avgbox(cval);
682 scalecolor(cval, 1./exposure);
683 pix2loc(hv, &inpres, xpos-xoff, ypos-yoff);
684 if (!gotview || viewray(rorg, rdir, &ourview, hv[0], hv[1]) < 0)
685 rorg[0] = rorg[1] = rorg[2] =
686 rdir[0] = rdir[1] = rdir[2] = 0.;
687
688 for (cp = tout; *cp; cp++) /* print what they asked for */
689 switch (*cp) {
690 case 'o': /* origin */
691 printf("%e %e %e ", rorg[0], rorg[1], rorg[2]);
692 break;
693 case 'd': /* direction */
694 printf("%e %e %e ", rdir[0], rdir[1], rdir[2]);
695 break;
696 case 'v': /* radiance value */
697 printf("%e %e %e ", colval(cval,RED),
698 colval(cval,GRN), colval(cval,BLU));
699 break;
700 case 'l': /* luminance */
701 printf("%e ", luminance(cval));
702 break;
703 case 'p': /* pixel position */
704 printf("%d %d ", (int)(hv[0]*inpres.xr),
705 (int)(hv[1]*inpres.yr));
706 break;
707 }
708 putchar('\n');
709 fflush(stdout);
710 return(0);
711 }
712
713
714 static int
715 docom( /* execute command */
716 XKeyPressedEvent *ekey
717 )
718 {
719 char buf[80];
720 COLOR cval;
721 XColor cvx;
722 int com, n;
723 double comp;
724 RREAL hv[2];
725
726 n = XLookupString(ekey, buf, sizeof(buf), NULL, NULL);
727 if (n == 0)
728 return(0);
729 com = buf[0];
730 switch (com) { /* interpret command */
731 case 'q':
732 case 'Q':
733 case CTRL('D'): /* quit */
734 quiterr(NULL);
735 case '\n':
736 case '\r':
737 case 'l':
738 case 'c': /* value */
739 if (!avgbox(cval))
740 return(-1);
741 switch (com) {
742 case '\n':
743 case '\r': /* radiance */
744 sprintf(buf, "%.3f", intens(cval)/exposure);
745 break;
746 case 'l': /* luminance */
747 sprintf(buf, "%.1fL", luminance(cval)/exposure);
748 break;
749 case 'c': /* color */
750 comp = pow(2.0, (double)scale);
751 sprintf(buf, "(%.2f,%.2f,%.2f)",
752 colval(cval,RED)*comp,
753 colval(cval,GRN)*comp,
754 colval(cval,BLU)*comp);
755 break;
756 }
757 XDrawImageString(thedisplay, wind, ourgc,
758 bbox.xmin, bbox.ymin+bbox.ysiz, buf, strlen(buf));
759 return(0);
760 case 'i': /* identify (contour) */
761 if (ourras->pixels == NULL)
762 return(-1);
763 n = ourdata[ekey->x-xoff+xmax*(ekey->y-yoff)];
764 n = ourras->pmap[n];
765 cvx.pixel = ourras->cdefs[n].pixel;
766 cvx.red = random() & 65535;
767 cvx.green = random() & 65535;
768 cvx.blue = random() & 65535;
769 cvx.flags = DoRed|DoGreen|DoBlue;
770 XStoreColor(thedisplay, ourras->cmap, &cvx);
771 return(0);
772 case 'p': /* position */
773 pix2loc(hv, &inpres, ekey->x-xoff, ekey->y-yoff);
774 sprintf(buf, "(%d,%d)", (int)(hv[0]*inpres.xr),
775 (int)(hv[1]*inpres.yr));
776 XDrawImageString(thedisplay, wind, ourgc, ekey->x, ekey->y,
777 buf, strlen(buf));
778 return(0);
779 case 't': /* trace */
780 return(traceray(ekey->x, ekey->y));
781 case 'a': /* auto exposure */
782 if (fname == NULL)
783 return(-1);
784 tmflags = TM_F_CAMERA;
785 strcpy(buf, "auto exposure...");
786 goto remap;
787 case 'h': /* human response */
788 if (fname == NULL)
789 return(-1);
790 tmflags = TM_F_HUMAN;
791 strcpy(buf, "human exposure...");
792 goto remap;
793 case '=': /* adjust exposure */
794 case '@': /* adaptation level */
795 if (!avgbox(cval))
796 return(-1);
797 comp = bright(cval);
798 if (comp < 1e-20) {
799 XBell(thedisplay, 0);
800 return(-1);
801 }
802 if (com == '@')
803 comp = 106./exposure/
804 pow(1.219+pow(comp*WHTEFFICACY/exposure,.4),2.5);
805 else
806 comp = .5/comp;
807 comp = log(comp)/.69315 - scale;
808 n = comp < 0 ? comp-.5 : comp+.5 ; /* round */
809 if (tmflags != TM_F_LINEAR)
810 tmflags = TM_F_LINEAR; /* turn off tone mapping */
811 else {
812 if (n == 0) /* else check if any change */
813 return(0);
814 scale_rcolors(ourras, pow(2.0, (double)n));
815 }
816 scale += n;
817 sprintf(buf, "%+d", scale);
818 remap:
819 XDrawImageString(thedisplay, wind, ourgc,
820 bbox.xmin, bbox.ymin+bbox.ysiz, buf, strlen(buf));
821 XFlush(thedisplay);
822 /* free(ourdata); This is done in XDestroyImage()! */
823 free_raster(ourras);
824 getras();
825 /* fall through */
826 case CTRL('R'): /* redraw */
827 case CTRL('L'):
828 unmap_rcolors(ourras);
829 XClearWindow(thedisplay, wind);
830 map_rcolors(ourras, wind);
831 if (fast)
832 make_rpixmap(ourras, wind);
833 redraw(0, 0, width, height);
834 return(0);
835 case 'f': /* turn on fast redraw */
836 fast = 1;
837 make_rpixmap(ourras, wind);
838 return(0);
839 case 'F': /* turn off fast redraw */
840 fast = 0;
841 free_rpixmap(ourras);
842 return(0);
843 case '0': /* recenter origin */
844 if ((xoff == 0) & (yoff == 0))
845 return(0);
846 xoff = yoff = 0;
847 XClearWindow(thedisplay, wind);
848 redraw(0, 0, width, height);
849 return(0);
850 case ' ': /* clear */
851 redraw(bbox.xmin, bbox.ymin, bbox.xsiz, bbox.ysiz);
852 return(0);
853 default:
854 XBell(thedisplay, 0);
855 return(-1);
856 }
857 }
858
859
860 static void
861 moveimage( /* shift the image */
862 XButtonPressedEvent *ebut
863 )
864 {
865 XEvent e;
866 int mxo, myo;
867
868 XMaskEvent(thedisplay, ButtonReleaseMask|ButtonMotionMask, &e);
869 while (e.type == MotionNotify) {
870 mxo = e.xmotion.x;
871 myo = e.xmotion.y;
872 revline(ebut->x, ebut->y, mxo, myo);
873 revbox(xoff+mxo-ebut->x, yoff+myo-ebut->y,
874 xoff+mxo-ebut->x+xmax, yoff+myo-ebut->y+ymax);
875 XMaskEvent(thedisplay,ButtonReleaseMask|ButtonMotionMask,&e);
876 revline(ebut->x, ebut->y, mxo, myo);
877 revbox(xoff+mxo-ebut->x, yoff+myo-ebut->y,
878 xoff+mxo-ebut->x+xmax, yoff+myo-ebut->y+ymax);
879 }
880 xoff += e.xbutton.x - ebut->x;
881 yoff += e.xbutton.y - ebut->y;
882 XClearWindow(thedisplay, wind);
883 redraw(0, 0, width, height);
884 }
885
886
887 static void
888 getbox( /* get new bbox */
889 XButtonPressedEvent *ebut
890 )
891 {
892 XEvent e;
893
894 XMaskEvent(thedisplay, ButtonReleaseMask|ButtonMotionMask, &e);
895 while (e.type == MotionNotify) {
896 revbox(ebut->x, ebut->y, bbox.xmin = e.xmotion.x, bbox.ymin = e.xmotion.y);
897 XMaskEvent(thedisplay,ButtonReleaseMask|ButtonMotionMask,&e);
898 revbox(ebut->x, ebut->y, bbox.xmin, bbox.ymin);
899 }
900 bbox.xmin = e.xbutton.x<0 ? 0 : (e.xbutton.x>=width ? width-1 : e.xbutton.x);
901 bbox.ymin = e.xbutton.y<0 ? 0 : (e.xbutton.y>=height ? height-1 : e.xbutton.y);
902 if (bbox.xmin > ebut->x) {
903 bbox.xsiz = bbox.xmin - ebut->x + 1;
904 bbox.xmin = ebut->x;
905 } else {
906 bbox.xsiz = ebut->x - bbox.xmin + 1;
907 }
908 if (bbox.ymin > ebut->y) {
909 bbox.ysiz = bbox.ymin - ebut->y + 1;
910 bbox.ymin = ebut->y;
911 } else {
912 bbox.ysiz = ebut->y - bbox.ymin + 1;
913 }
914 }
915
916
917 static void
918 trackrays( /* trace rays as mouse moves */
919 XButtonPressedEvent *ebut
920 )
921 {
922 XEvent e;
923 unsigned long lastrept;
924
925 traceray(ebut->x, ebut->y);
926 lastrept = ebut->time;
927 XMaskEvent(thedisplay, ButtonReleaseMask|ButtonMotionMask, &e);
928 while (e.type == MotionNotify) {
929 if (e.xmotion.time >= lastrept + tinterv) {
930 traceray(e.xmotion.x, e.xmotion.y);
931 lastrept = e.xmotion.time;
932 }
933 XMaskEvent(thedisplay,ButtonReleaseMask|ButtonMotionMask,&e);
934 }
935 }
936
937
938 static void
939 revbox( /* draw bbox with reversed lines */
940 int x0,
941 int y0,
942 int x1,
943 int y1
944 )
945 {
946 revline(x0, y0, x1, y0);
947 revline(x0, y1, x1, y1);
948 revline(x0, y0, x0, y1);
949 revline(x1, y0, x1, y1);
950 }
951
952
953 static void
954 colavg(
955 COLR *scn,
956 int n,
957 void *cavg
958 )
959 {
960 COLOR col;
961
962 while (n--) {
963 colr_color(col, *scn);
964 addcolor((COLORV*)cavg, col);
965 scn++;
966 }
967 }
968
969
970 static int
971 avgbox( /* average color over current bbox */
972 COLOR cavg
973 )
974 {
975 double d;
976 int rval;
977
978 setcolor(cavg, 0., 0., 0.);
979 rval = dobox(colavg, cavg);
980 if (rval > 0) {
981 d = 1./rval;
982 scalecolor(cavg, d);
983 }
984 return(rval);
985 }
986
987
988 static int
989 dobox( /* run function over bbox */
990 doboxf_t *f, /* function to call for each subscan */
991 void *p /* pointer to private data */
992 )
993 {
994 int left, right, top, bottom;
995 int y;
996
997 left = bbox.xmin - xoff;
998 right = left + bbox.xsiz;
999 if (left < 0)
1000 left = 0;
1001 if (right > xmax)
1002 right = xmax;
1003 if (left >= right)
1004 return(0);
1005 top = bbox.ymin - yoff;
1006 bottom = top + bbox.ysiz;
1007 if (top < 0)
1008 top = 0;
1009 if (bottom > ymax)
1010 bottom = ymax;
1011 if (top >= bottom)
1012 return(0);
1013 for (y = top; y < bottom; y++) {
1014 if (getscan(y) < 0)
1015 return(-1);
1016 (*f)(scanline+left, right-left, p);
1017 }
1018 return((right-left)*(bottom-top));
1019 }
1020
1021
1022 static void
1023 addfix( /* add fixation points to histogram */
1024 COLR *scn,
1025 int n,
1026 void *p
1027 )
1028 {
1029 TMstruct * tms = (TMstruct *)p;
1030
1031 if (tmCvColrs(tms, lscan, TM_NOCHROM, scn, n))
1032 goto tmerr;
1033 if (tmAddHisto(tms, lscan, n, FIXWEIGHT))
1034 goto tmerr;
1035 return;
1036 tmerr:
1037 quiterr("tone mapping error");
1038 }
1039
1040
1041 static void
1042 make_tonemap(void) /* initialize tone mapping */
1043 {
1044 int flags, y;
1045
1046 if (tmflags != TM_F_LINEAR && fname == NULL) {
1047 fprintf(stderr, "%s: cannot adjust tone of standard input\n",
1048 progname);
1049 tmflags = TM_F_LINEAR;
1050 }
1051 if (tmflags == TM_F_LINEAR) { /* linear with clamping */
1052 setcolrcor(pow, 1.0/gamcor);
1053 return;
1054 }
1055 flags = tmflags; /* histogram adjustment */
1056 if (greyscale) flags |= TM_F_BW;
1057 if (tmGlobal != NULL) { /* reuse old histogram if one */
1058 tmDone(tmCurrent); tmCurrent = NULL;
1059 tmGlobal->flags = flags;
1060 } else { /* else initialize */
1061 if ((lscan = (TMbright *)malloc(xmax*sizeof(TMbright))) == NULL)
1062 goto memerr;
1063 if (greyscale) {
1064 cscan = TM_NOCHROM;
1065 if ((pscan = (uby8 *)malloc(sizeof(uby8)*xmax)) == NULL)
1066 goto memerr;
1067 } else if ((pscan=cscan = (uby8 *)malloc(3*sizeof(uby8)*xmax))
1068 == NULL)
1069 goto memerr;
1070 /* initialize tm library */
1071 tmGlobal = tmInit(flags, stdprims, gamcor);
1072 if (tmGlobal == NULL)
1073 goto memerr;
1074 if (tmSetSpace(tmGlobal, stdprims, WHTEFFICACY/exposure))
1075 goto tmerr;
1076 /* compute picture histogram */
1077 for (y = 0; y < ymax; y++) {
1078 getscan(y);
1079 if (tmCvColrs(tmGlobal, lscan, TM_NOCHROM,
1080 scanline, xmax))
1081 goto tmerr;
1082 if (tmAddHisto(tmGlobal, lscan, xmax, 1))
1083 goto tmerr;
1084 }
1085 }
1086 tmCurrent = tmDup(tmGlobal); /* add fixations to duplicate map */
1087 dobox(addfix, tmCurrent);
1088 /* (re)compute tone mapping */
1089 if (tmComputeMapping(tmCurrent, gamcor, 0., 0.))
1090 goto tmerr;
1091 return;
1092 memerr:
1093 quiterr("out of memory in make_tonemap");
1094 tmerr:
1095 quiterr("tone mapping error");
1096 }
1097
1098
1099 static void
1100 tmap_colrs( /* apply tone mapping to scanline */
1101 COLR *scn,
1102 int len
1103 )
1104 {
1105 uby8 *ps;
1106
1107 if (tmflags == TM_F_LINEAR) {
1108 if (scale)
1109 shiftcolrs(scn, len, scale);
1110 colrs_gambs(scn, len);
1111 return;
1112 }
1113 if (len > xmax)
1114 quiterr("code error 1 in tmap_colrs");
1115 if (tmCvColrs(tmCurrent, lscan, cscan, scn, len))
1116 goto tmerr;
1117 if (tmMapPixels(tmCurrent, pscan, lscan, cscan, len))
1118 goto tmerr;
1119 ps = pscan;
1120 if (greyscale)
1121 while (len--) {
1122 scn[0][RED] = scn[0][GRN] = scn[0][BLU] = *ps++;
1123 scn[0][EXP] = COLXS;
1124 scn++;
1125 }
1126 else
1127 while (len--) {
1128 scn[0][RED] = *ps++;
1129 scn[0][GRN] = *ps++;
1130 scn[0][BLU] = *ps++;
1131 scn[0][EXP] = COLXS;
1132 scn++;
1133 }
1134 return;
1135 tmerr:
1136 quiterr("tone mapping error");
1137 }
1138
1139
1140 static void
1141 getmono(void) /* get monochrome data */
1142 {
1143 unsigned char *dp;
1144 int x, err;
1145 int y, errp;
1146 short *cerr;
1147
1148 if ((cerr = (short *)calloc(xmax,sizeof(short))) == NULL)
1149 quiterr("out of memory in getmono");
1150 dp = ourdata - 1;
1151 for (y = 0; y < ymax; y++) {
1152 getscan(y);
1153 add2icon(y, scanline);
1154 normcolrs(scanline, xmax, scale);
1155 err = 0;
1156 for (x = 0; x < xmax; x++) {
1157 if (!(x&7))
1158 *++dp = 0;
1159 errp = err;
1160 err += normbright(scanline[x]) + cerr[x];
1161 if (err > 127)
1162 err -= 255;
1163 else
1164 *dp |= 1<<(7-(x&07));
1165 err /= 3;
1166 cerr[x] = err + errp;
1167 }
1168 }
1169 free(cerr);
1170 }
1171
1172
1173 static void
1174 add2icon( /* add a scanline to our icon data */
1175 int y,
1176 COLR *scan
1177 )
1178 {
1179 static short cerr[ICONSIZ];
1180 static int ynext;
1181 static char *dp;
1182 COLR clr;
1183 int err;
1184 int x, ti;
1185 int errp;
1186
1187 if (iconheight == 0) { /* initialize */
1188 if (xmax <= ICONSIZ && ymax <= ICONSIZ) {
1189 iconwidth = xmax;
1190 iconheight = ymax;
1191 } else if (xmax > ymax) {
1192 iconwidth = ICONSIZ;
1193 iconheight = ICONSIZ*ymax/xmax;
1194 if (iconheight < 1)
1195 iconheight = 1;
1196 } else {
1197 iconwidth = ICONSIZ*xmax/ymax;
1198 if (iconwidth < 1)
1199 iconwidth = 1;
1200 iconheight = ICONSIZ;
1201 }
1202 ynext = 0;
1203 dp = icondata - 1;
1204 }
1205 if (y < ynext*ymax/iconheight) /* skip this one */
1206 return;
1207 err = 0;
1208 for (x = 0; x < iconwidth; x++) {
1209 if (!(x&7))
1210 *++dp = 0;
1211 errp = err;
1212 ti = x*xmax/iconwidth;
1213 copycolr(clr, scan[ti]);
1214 normcolrs(&clr, 1, scale);
1215 err += normbright(clr) + cerr[x];
1216 if (err > 127)
1217 err -= 255;
1218 else
1219 *dp |= 1<<(x&07);
1220 err /= 3;
1221 cerr[x] = err + errp;
1222 }
1223 ynext++;
1224 }
1225
1226
1227 static void
1228 getfull(void) /* get full (24-bit) data */
1229 {
1230 int y;
1231 uint32 *dp;
1232 uint16 *dph;
1233 int x;
1234 /* initialize tone mapping */
1235 make_tonemap();
1236 /* read and convert file */
1237 dp = (uint32 *)ourdata;
1238 dph = (uint16 *)ourdata;
1239 for (y = 0; y < ymax; y++) {
1240 getscan(y);
1241 add2icon(y, scanline);
1242 tmap_colrs(scanline, xmax);
1243 switch (ourras->image->blue_mask) {
1244 case 0xff: /* 24-bit RGB */
1245 for (x = 0; x < xmax; x++)
1246 *dp++ = (uint32)scanline[x][RED] << 16 |
1247 (uint32)scanline[x][GRN] << 8 |
1248 (uint32)scanline[x][BLU] ;
1249 break;
1250 case 0xff0000: /* 24-bit BGR */
1251 for (x = 0; x < xmax; x++)
1252 *dp++ = (uint32)scanline[x][RED] |
1253 (uint32)scanline[x][GRN] << 8 |
1254 (uint32)scanline[x][BLU] << 16 ;
1255 break;
1256 #if 0
1257 case 0x1f: /* 15-bit RGB */
1258 for (x = 0; x < xmax; x++)
1259 *dph++ = (scanline[x][RED] << 7 & 0x7c00) |
1260 (scanline[x][GRN] << 2 & 0x3e0) |
1261 (unsigned)scanline[x][BLU] >> 3 ;
1262 break;
1263 case 0x7c00: /* 15-bit BGR */
1264 for (x = 0; x < xmax; x++)
1265 *dph++ = (unsigned)scanline[x][RED] >> 3 |
1266 (scanline[x][GRN] << 2 & 0x3e0) |
1267 (scanline[x][BLU] << 7 & 0x7c00) ;
1268 break;
1269 #endif
1270 default: /* unknown */
1271 if (ourvis.depth > 16)
1272 for (x = 0; x < xmax; x++) {
1273 *dp = ourras->image->red_mask &
1274 ourras->image->red_mask*scanline[x][RED]/255;
1275 *dp |= ourras->image->green_mask &
1276 ourras->image->green_mask*scanline[x][GRN]/255;
1277 *dp++ |= ourras->image->blue_mask &
1278 ourras->image->blue_mask*scanline[x][BLU]/255;
1279 }
1280 else
1281 for (x = 0; x < xmax; x++) {
1282 *dph = ourras->image->red_mask &
1283 ourras->image->red_mask*scanline[x][RED]/255;
1284 *dph |= ourras->image->green_mask &
1285 ourras->image->green_mask*scanline[x][GRN]/255;
1286 *dph++ |= ourras->image->blue_mask &
1287 ourras->image->blue_mask*scanline[x][BLU]/255;
1288 }
1289 break;
1290 }
1291 }
1292 }
1293
1294
1295 static void
1296 getgrey(void) /* get greyscale data */
1297 {
1298 int y;
1299 unsigned char *dp;
1300 int x;
1301 /* initialize tone mapping */
1302 make_tonemap();
1303 /* read and convert file */
1304 dp = ourdata;
1305 for (y = 0; y < ymax; y++) {
1306 getscan(y);
1307 add2icon(y, scanline);
1308 tmap_colrs(scanline, xmax);
1309 if (maxcolors < 256)
1310 for (x = 0; x < xmax; x++)
1311 *dp++ = ((int32)scanline[x][GRN] *
1312 maxcolors + maxcolors/2) >> 8;
1313 else
1314 for (x = 0; x < xmax; x++)
1315 *dp++ = scanline[x][GRN];
1316 }
1317 for (x = 0; x < maxcolors; x++)
1318 clrtab[x][RED] = clrtab[x][GRN] =
1319 clrtab[x][BLU] = ((int32)x*256 + 128)/maxcolors;
1320 }
1321
1322
1323 static void
1324 getmapped(void) /* get color-mapped data */
1325 {
1326 int y;
1327 /* make sure we can do it first */
1328 if (fname == NULL)
1329 quiterr("cannot map colors from standard input");
1330 /* initialize tone mapping */
1331 make_tonemap();
1332 /* make histogram */
1333 if (new_histo((int32)xmax*ymax) == -1)
1334 quiterr("cannot initialize histogram");
1335 for (y = 0; y < ymax; y++) {
1336 if (getscan(y) < 0)
1337 break;
1338 add2icon(y, scanline);
1339 tmap_colrs(scanline, xmax);
1340 cnt_colrs(scanline, xmax);
1341 }
1342 /* map pixels */
1343 if (!new_clrtab(maxcolors))
1344 quiterr("cannot create color map");
1345 for (y = 0; y < ymax; y++) {
1346 getscan(y);
1347 tmap_colrs(scanline, xmax);
1348 if (dither)
1349 dith_colrs(ourdata+y*xmax, scanline, xmax);
1350 else
1351 map_colrs(ourdata+y*xmax, scanline, xmax);
1352 }
1353 }
1354
1355
1356 static void
1357 scale_rcolors( /* scale color map */
1358 XRASTER *xr,
1359 double sf
1360 )
1361 {
1362 int i;
1363 long maxv;
1364
1365 if (xr->pixels == NULL)
1366 return;
1367
1368 sf = pow(sf, 1.0/gamcor);
1369 maxv = 65535/sf;
1370
1371 for (i = xr->ncolors; i--; ) {
1372 xr->cdefs[i].red = xr->cdefs[i].red > maxv ?
1373 65535 :
1374 xr->cdefs[i].red * sf;
1375 xr->cdefs[i].green = xr->cdefs[i].green > maxv ?
1376 65535 :
1377 xr->cdefs[i].green * sf;
1378 xr->cdefs[i].blue = xr->cdefs[i].blue > maxv ?
1379 65535 :
1380 xr->cdefs[i].blue * sf;
1381 }
1382 XStoreColors(thedisplay, xr->cmap, xr->cdefs, xr->ncolors);
1383 }
1384
1385
1386 static int
1387 getscan(
1388 int y
1389 )
1390 {
1391 static int trunced = -1; /* truncated file? */
1392 skipit:
1393 if (y == cury-1) /* already got it? */
1394 return(0);
1395 if ((trunced >= 0) & (y >= trunced)) {
1396 memset(scanline, 0, xmax*sizeof(COLR));
1397 return(-1);
1398 }
1399 if (y != cury) {
1400 if (scanpos == NULL || scanpos[y] < 0)
1401 return(-1);
1402 if (fseek(fin, scanpos[y], 0) < 0)
1403 quiterr("fseek error");
1404 cury = y;
1405 } else if (scanpos != NULL && scanpos[y] < 0)
1406 scanpos[y] = ftell(fin);
1407
1408 if (fread2colrs(scanline, xmax, fin, NCSAMP, WLPART) < 0) {
1409 fprintf(stderr, "%s: %s: unfinished picture\n",
1410 progname, fname==NULL?"<stdin>":fname);
1411 trunced = y;
1412 goto skipit;
1413 }
1414 cury++;
1415 return(0);
1416 }