1 /*
2  * This software is copyrighted as noted below.  It may be freely copied,
3  * modified, and redistributed, provided that the copyright notice is
4  * preserved on all copies.
5  *
6  * There is no warranty or other guarantee of fitness for this software,
7  * it is provided solely "as is".  Bug reports or fixes may be sent
8  * to the author, who may or may not act on them as he desires.
9  *
10  * You may not include this software in a program or other software product
11  * without supplying the source, or without informing the end-user that the
12  * source is available for no extra charge.
13  *
14  * If you modify this software, you should include a notice giving the
15  * name of the person performing the modification, the date of modification,
16  * and the reason for such modification.
17  */
18 /*
19  * getx10.c - Put RLE images on X display.
20  *
21  * Author:	Spencer W. Thomas
22  * 		Computer Science Dept.
23  * 		University of Utah
24  * Date:	Thu Feb 20 1986
25  * Copyright (c) 1986, University of Utah
26  *
27  */
28 #ifndef lint
29 static char rcs_ident[] = "$Id: getx10.c,v 3.0.1.1 1992/01/28 18:12:35 spencer Exp $";
30 #endif
31 
32 #include <stdio.h>
33 #include <math.h>
34 #include <X/Xlib.h>
35 #include "rle.h"
36 
37 /* Most that can be sent to X in one chunk */
38 #define MAXSEND	65535			/* 64K */
39 
40 /*
41  * Basic magic square for dithering
42  */
43 int dm16[16][16];
44 
45 /* define arrow cursor for zoom mode */
46 #define arrow_width 16
47 #define arrow_height 16
48 #define arrow_x_hot 4
49 #define arrow_y_hot 1
50 static short arrow_bits[] = {
51    0x0000, 0x0010, 0x0030, 0x0070,
52    0x00f0, 0x01f0, 0x03f0, 0x07f0,
53    0x0ff0, 0x01f0, 0x03b0, 0x0310,
54    0x0700, 0x0600, 0x0600, 0x0000};
55 #define arrow_mask_width 16
56 #define arrow_mask_height 16
57 static short arrow_mask_bits[] = {
58    0x0018, 0x0038, 0x0078, 0x00f8,
59    0x01f8, 0x03f8, 0x07f8, 0x0ff8,
60    0x1ff8, 0x1ff8, 0x07f8, 0x07b8,
61    0x0f98, 0x0f00, 0x0f00, 0x0f00};
62 Cursor arrow_curs;
63 
64 /*
65  * Color map, gamma correction map, and lookup tables
66  */
67 Color colmap[256];
68 int gammamap[256];
69 rle_pixel ** in_cmap;
70 int modN[256], divN[256];
71 int bwflag = 0;			/* if non zero, dither in B&W
72 				 * Value of 2 means 1-bit system
73 				 */
74 
75 /*
76  * Number of color map levels, (square and cube), and number of gradations
77  * per level.
78  */
79 int levels = 0, levelsq, levelsc;
80 
81 /*
82  * Global variables
83  */
84 rle_hdr hdr;
85 double disp_gam = 2.5;		/* default gammas for display and image */
86 double img_gam = 1.0;
87 int iflag = 0;			/* flag to tell if gamma was on command line */
88 int setbg = 0;			/* set root background to image? */
89 int mapflg = 0;			/* flag to just load color map */
90 int usemap = 0;			/* Use given color map, don't dither */
91 int forkflg = 0;		/* No fork allows zoom info (middle button) */
92 int zoomflg = 0;		/* build zoom window */
93 
94 Display * dpy;
95 Window fbwin, iconwin, zoomwin;
96 int iconfact, iconrow, iconscan, iconbyte;
97 int zoomfact = 8;
98 int zoom_x_dim = 15;
99 int zoom_y_dim = 15;
100 int zoom_x_center, zoom_y_center;
101 int nscan, nrow;			/* size of window */
102 int nbyte;			/* size of bitrow for 1-bit displays */
103 
104 int dbg = 0;			/* set if debug mode */
105 
106 unsigned char *buffer, *iconbuf;	/* data storage pointers */
107 
108 /*****************************************************************
109  * TAG( main )
110  *
111  * Usage:
112  *	getx10 [-{bB}] [-z] [-m] [-f] [-p] [-D] [-d display]
113  *		[-= window-geometry] [-{iI} gamma] [-g gamma] [file]
114  * Inputs:
115  *	-b:		Set the root window background to the resultant image.
116  *	-B:		Set the root background, but don't display the image
117  *			in a separate window.
118  *	-z:		Create Zoom window also.  Any button in image recenters
119  *			zoom.  Drag in image resizes zoom window to enclose
120  *			region.  Left in zoom window decreases zoom factor.
121  *			Right increases zoom factor.  Middle prints position
122  *			info to the terminal, but only if -f is on.
123  *	-m:		Just load color map and exit.
124  * 	-f:		Don't fork after putting image on screen.
125  *	-p:		Don't use off-screen memory to speed up redraw.
126  *	-D:		Debug mode: print input file as read.
127  *	-w:		Black & white: reduce color images to B&W before
128  *			display.  Advantage is that smoother shading can
129  *			be achieved.
130  *	-c:		Use colors specified in color map.  Will try to load
131  *			entire color map.  No dithering will be done
132  *			when this option is specified.  Actual number
133  *			of entries in color map may be given as a
134  *			picture comment:
135  *			color_map_length=<number of entries in color map>.
136  *	-d display:	Specify display name.
137  *	-= window_geometry:
138  *			Specify window geometry (but min size set by file).
139  *	-i gamma:	Specify gamma of image. (default 1.0)
140  *	-I gamma:	Specify gamma of display image was computed for.
141  * 	getx10 will also read picture comments from the input file to determine
142  *			the image gamma.  These are
143  *	image_gamma=	gamma of image (equivalent to -i)
144  *	display_gamma=	gamma of display image was computed for.
145  *			Command line arguments override values in the file.
146  *
147  *	-g gamma:	Specify gamma of display. (default 2.5)
148  *	file:		Input Run Length Encoded file. Uses stdin if not
149  *			specified.
150  * Outputs:
151  * 	Puts image on screen.
152  * Assumptions:
153  * 	Input file is in RLE format.
154  * Algorithm:
155  *	[None]
156  */
157 
main(argc,argv)158 main(argc, argv)
159 char **argv;
160 {
161     char ** infnames = NULL, *infname = NULL, *display_name = NULL,
162 	* window_geometry = NULL;
163     FILE * infile = stdin;
164     int nfile = 0, use_pix = 0,
165 	dflag = 0, gflag = 0, wflag = 0;
166 
167     if ( scanargs( argc, argv,
168 		   "% Bb%- m%- f%- p%- cWw%- D%- n%-levels!d d%-display!s \n\
169 \t=%-window-geometry!s Ii%-gamma!F g%-gamma!F z%- file%s",
170 		   &setbg, &mapflg, &forkflg, &use_pix, &bwflag, &dbg,
171 		   &levels, &levels,
172 		   &dflag, &display_name,
173 		   &wflag, &window_geometry,
174 		   &iflag, &img_gam,
175 		   &gflag, &disp_gam,
176 		   &zoomflg,
177 		   &infname ) == 0 )
178 	exit( 1 );
179 
180     hdr = *rle_hdr_init( (rle_hdr *)NULL );
181     rle_names( &hdr, cmd_name( argv ), infname );
182 
183     use_pix = ! use_pix;
184     if ( setbg & !use_pix )
185     {
186 	fprintf( stderr, "Can't specify -p with -b, -p ignored\n" );
187 	use_pix = 1;
188     }
189     if ( setbg == 2 )
190 	forkflg = 1;
191 
192     if ( iflag == 1 )		/* -i */
193 	img_gam = 1.0 / img_gam;
194 
195     if ( bwflag == 4 )		/* -c */
196     {
197 	bwflag = 0;
198 	usemap = 1;
199     }
200 
201     /* Would like to be able to use multiple files, but haven't
202      * figured how to get X to let us.  So, use this kludge for now.
203      */
204     if ( infname != NULL )
205     {
206 	infnames = &infname;
207 	nfile = 1;
208     }
209 
210     /*
211      * For each file, display it.
212      */
213     do {
214 	if ( nfile > 0 )
215 	{
216             infile = rle_open_f(hdr.cmd, *infnames, "r");
217 	    get_pic( infile, *infnames, display_name,
218 		     window_geometry );
219 	    fclose( infile );
220 	    infnames++;
221 	    nfile--;
222 	}
223 	else
224 	    get_pic( stdin, NULL, display_name,
225 		     window_geometry );
226 	if ( ! forkflg )
227 	{
228 	    if ( fork() == 0 )
229 	    {
230 		/*
231 		 * Get rid of std fds so rshd will go
232 		 * away.
233 		 */
234 		close( 0 );
235 		close( 1 );
236 		close( 2 );
237 		update_pic( use_pix );
238 		break;
239 	    }
240 	}
241 	else
242 	{
243 	    update_pic( use_pix );
244 	}
245     } while ( nfile > 0 );
246     exit( 0 );
247     /*	XCloseDisplay( dpy );*/
248 }
249 
250 /*
251  * Read an image from the input file and display it.
252  */
get_pic(infile,infname,display_name,window_geometry)253 get_pic( infile, infname, display_name, window_geometry )
254 FILE * infile;
255 char * infname;
256 char * display_name;
257 char * window_geometry;
258 {
259     register int    i,
260                     y;
261     int		    ncolors,
262 		    xmin_original;
263     unsigned char  *scan[3];
264 
265     /*
266      * Read setup info from file.
267      */
268     hdr.rle_file = infile;
269     if ( !mapflg || usemap )
270 	rle_get_setup_ok(&hdr, NULL, NULL);
271 
272     if ( dbg )
273 	rle_debug( 1 );
274 
275     /* We're only interested in R, G, & B */
276     RLE_CLR_BIT(hdr, RLE_ALPHA);
277     for (i = 3; i < hdr.ncolors; i++)
278 	RLE_CLR_BIT(hdr, i);
279     ncolors = hdr.ncolors > 3 ? 3 : hdr.ncolors;
280 
281     /*
282      * Open display first time through.
283      */
284     if (dpy == NULL)
285     {
286 	dpy = XOpenDisplay(display_name);
287 	if (dpy == NULL)
288 	{
289 	    fprintf(stderr, "%s: Can't open display %s\n",
290 		    hdr.cmd, display_name ? "" : display_name);
291 	    exit(1);
292 	}
293 	if ( DisplayPlanes() == 1 )	/* b&w display */
294 	    bwflag = 2;
295     }
296 
297     /* If no image gamma on command line, check comments in file */
298     if ( ! iflag )
299     {
300 	char * v;
301 	if ( (v = rle_getcom( "image_gamma", &hdr )) != NULL )
302 	{
303 	    img_gam = atof( v );
304 	    /* Protect against bogus information */
305 	    if ( img_gam == 0.0 )
306 		img_gam = 1.0;
307 	    else
308 		img_gam = 1.0 / img_gam;
309 	}
310 	else if ( (v = rle_getcom( "display_gamma", &hdr )) != NULL )
311 	{
312 	    img_gam = atof( v );
313 	    /* Protect */
314 	    if ( img_gam == 0.0 )
315 		img_gam = 1.0;
316 	}
317     }
318 
319     /*
320      * Set up the color map.
321      */
322     /* Input map, at least 3 channels */
323     in_cmap = buildmap( &hdr, 3, img_gam, 1.0 );
324     /* Get X color map */
325     if ( usemap )
326 	load_x_map();
327     else
328 	init_color();
329 
330     if ( mapflg )
331 	exit( 0 );
332 
333     /*
334      * Compute image size and allocate storage.
335      */
336     nrow = (hdr.xmax - hdr.xmin + 1);
337     if ( bwflag == 2 )
338 	nbyte = ((nrow + 15) / 16) * 2;	/* 1 bit display */
339     else
340 	nbyte = nrow;
341     nscan = (hdr.ymax - hdr.ymin + 1);
342     buffer = (unsigned char *) malloc(nbyte * nscan);
343 
344     /*
345      * Icon wants to be about 50 x 50.  Figure out how much smaller than the
346      * image this is.
347      */
348     iconfact = nrow / 50;
349     if (iconfact < nscan / 50)
350 	iconfact = nscan / 50;
351     if ( iconfact == 0 )
352 	iconfact = 1;
353     iconrow = (1 + nrow / iconfact);
354     iconscan = (1 + nscan / iconfact);
355     if ( bwflag == 2 )
356     {
357 	iconbuf = (unsigned char *)
358 	    malloc(BitmapSize( (1 + nrow / iconfact),
359 			       (1 + nscan / iconfact) ));
360 	iconbyte = 2 * ((iconrow + 15) / 16);
361     }
362     else
363     {
364 	iconbuf = (unsigned char *) malloc((1 + nrow / iconfact) *
365 					   (1 + nscan / iconfact));
366 	iconbyte = iconrow;
367     }
368 
369     /*
370      * Set up for rle_getrow.  Pretend image x origin is 0.
371      */
372     for (i = 0; i < 3; i++)
373 	scan[i] = (unsigned char *) malloc(nrow);
374     hdr.xmax -= hdr.xmin;
375     xmin_original = hdr.xmin;
376     hdr.xmin = 0;
377 
378     /*
379      * Get a window of the right size (user positions it with the mouse).
380      */
381     if ( setbg < 2 )
382 	create_window(nrow, nscan, window_geometry);
383 
384     /*
385      * For each scan line, dither it and display.
386      */
387     while ((y = rle_getrow(&hdr, scan)) <= hdr.ymax)
388     {
389 	if ( bwflag && ncolors > 1 )
390 	{
391 	    map_rgb_to_bw( scan[0], scan[1], scan[ncolors - 1], scan[0],
392 			   in_cmap, nrow );
393 	    /* Note: map_scanline only uses channel 0 for B&W */
394 	}
395 	else if ( bwflag )
396 	    for ( i = 0; i < nrow; i++ )
397 		scan[0][i] = in_cmap[0][scan[0][i]];
398 	else
399 	    for (i = 2; i >= ncolors; i--)
400 		bcopy(scan[0], scan[i], nrow);
401 	map_scanline(scan, nrow, 1, y,
402 		     &buffer[(hdr.ymax - y) * nbyte]);
403 	/* Subsample image to create icon */
404 	if ( (hdr.ymax - y) % iconfact == 0 )
405 	    map_scanline( scan, iconrow, iconfact,
406 			  (hdr.ymax - y) / iconfact,
407 			  &iconbuf[((hdr.ymax - y) / iconfact) *
408 				   iconbyte] );
409     	if ( setbg < 2 )
410 	    put_scanline(&buffer[(hdr.ymax - y) * nbyte], nrow, 0,
411 		     hdr.ymax - y );
412     }
413     /*
414      * Free temp storage
415      */
416     for (i = 0; i < 3; i++)
417 	free(scan[i]);
418 
419     hdr.xmin = xmin_original;
420     hdr.xmax += xmin_original;
421 }
422 /*
423  * Track events & redraw image when necessary.
424  */
update_pic(use_pix)425 update_pic( use_pix )
426 {
427     int             i,
428                     npix,
429                     pixscan,
430                     lastscan,
431                     gotpix;
432     long            bufsize,
433                     pixsize;
434     XEvent          rep;
435     XButtonEvent xkey;
436     XExposeEvent    xex;
437     int zoom_x_press, zoom_y_press;
438 
439     Pixmap(*pix)[] = 0;
440     Bitmap bm;
441 
442     if (zoomflg)
443     {
444 	zoom_x_center = nrow / 2;
445 	zoom_y_center = nscan / 2;
446 	update_zoom();
447     }
448 
449     /*
450      * If requested, use off screen pixmap to speed image redisplay. Need to
451      * allocate it in slices, since can't write whole image to X at once.
452      */
453     if (use_pix)
454     {
455 	npix = 1 + (nscan * nbyte) / MAXSEND;
456 	pixscan = MAXSEND / nbyte;
457 	pixsize = pixscan * nbyte;
458 	lastscan = nscan % pixscan;
459 
460 	pix = (Pixmap(*)[]) malloc(npix * sizeof(Pixmap));
461 	for (i = 0; i < npix; i++)
462 	    if ( bwflag != 2 )
463 		(*pix)[i] = XStorePixmapZ(nrow,
464 					  (i == npix - 1) ? lastscan : pixscan,
465 					  &buffer[i * nrow * pixscan]);
466 	    else
467 	    {
468 		 bm = XStoreBitmap(nrow,
469 				   (i == npix - 1) ? lastscan : pixscan,
470 				   &buffer[i * nbyte * pixscan]);
471 		 (*pix)[i] = XMakePixmap( bm, 1, 0 );
472 		 XFreeBitmap( bm );
473 	     }
474     }
475 
476     if ( setbg )
477     {
478 	XChangeBackground( RootWindow, (*pix)[0] );
479 	XClear( RootWindow );
480 	XFlush();
481     	if ( setbg == 2 )
482     	    exit( 0 );
483     }
484 
485     /*
486      * Basic event loop:  handle expose events on window & icon, and exit on
487      * (shifted) mouse button event.
488      */
489     for (;;)
490     {
491 	XNextEvent(&rep);
492 	if (rep.type == ButtonPressed)
493 	{
494 	    xkey = *(XButtonEvent *) &rep;
495 	    if (zoomflg && (xkey.window == fbwin))
496 	    {
497 		zoom_x_press = xkey.x;
498 		zoom_y_press = xkey.y;
499 	    }
500 	}
501 	else if (rep.type == ButtonReleased)
502 	{
503 	    xkey = *(XButtonEvent *) &rep;
504 	    if ( xkey.detail & ShiftMask )
505 	    {
506 		if (zoomflg && (xkey.window == zoomwin))
507 		{
508 		    /* get rid of just zoom window if shiftclicked */
509 		    XDestroyWindow(zoomwin);
510 		    zoomflg = 0;
511 		}
512 		else
513 		{
514 		    /* Shiftclick in fbwin means exit. */
515 		    break;
516 		}
517 	    }
518 	    if (zoomflg && (xkey.window == fbwin))
519 	    {
520 		zoom_x_center = xkey.x;
521 		zoom_y_center = xkey.y;
522 		if ((zoom_x_center != zoom_x_press) &&
523 		    (zoom_y_center != zoom_y_press))
524 		{
525 		    /* drag to define zoom region */
526 		    if (zoom_x_press < zoom_x_center)
527 		    {
528 			zoom_x_dim = (zoom_x_center - zoom_x_press);
529 			zoom_x_center = zoom_x_press + (zoom_x_dim / 2);
530 		    }
531 		    else
532 		    {
533 			zoom_x_dim = (zoom_x_press - zoom_x_center);
534 			zoom_x_center = zoom_x_center + (zoom_x_dim / 2);
535 		    }
536 		    if (zoom_y_press < zoom_y_center)
537 		    {
538 			zoom_y_dim = (zoom_y_center - zoom_y_press);
539 			zoom_y_center = zoom_y_press + (zoom_y_dim / 2);
540 		    }
541 		    else
542 		    {
543 			zoom_y_dim = (zoom_y_press - zoom_y_center);
544 			zoom_y_center = zoom_y_center + (zoom_y_dim / 2);
545 		    }
546 		    XChangeWindow(zoomwin, zoomfact * zoom_x_dim,
547 				  zoomfact * zoom_y_dim);
548 		    XSetResizeHint( zoomwin, 0, 0, zoomfact, zoomfact );
549 		}
550 		update_zoom();
551 	    }
552 	    else if (zoomflg && (xkey.window == zoomwin))
553 	    {
554 		if (xkey.detail & RightMask)
555 		{
556 		    /* increase zoom factor */
557 		    zoomfact += 1;
558 		    XChangeWindow(zoomwin, zoomfact * zoom_x_dim,
559 				  zoomfact * zoom_y_dim);
560 		    XSetResizeHint( zoomwin, 0, 0, zoomfact, zoomfact );
561 		}
562 		else if (xkey.detail & LeftMask)
563 		{
564 		    /* decrease zoom factor */
565 		    if (zoomfact > 1)
566 		    {
567 			zoomfact -= 1;
568 			XChangeWindow(zoomwin, zoomfact * zoom_x_dim,
569 				      zoomfact * zoom_y_dim);
570 			XSetResizeHint( zoomwin, 0, 0, zoomfact, zoomfact );
571 		    }
572 		}
573 		else if (xkey.detail & MiddleMask)
574 		{
575 		    if (forkflg)
576 		    {
577 			/* can only report pixel status when not forking */
578 			fprintf(stderr, "Position: (%d, %d)\n",
579 				hdr.xmin +
580 				xkey.x / zoomfact + (zoom_x_center
581 						     - (zoom_x_dim / 2)),
582 				(hdr.ymax
583 				 - xkey.y / zoomfact
584 				 - (zoom_y_center - (zoom_y_dim / 2))));
585 			fprintf(stderr, "Image crop: (%d, %d) to (%d, %d)\n",
586 				hdr.xmin, hdr.ymin,
587 				hdr.xmax, hdr.ymax);
588 			fprintf(stderr, "Zoom crop: (%d, %d) to (%d, %d)\n",
589 				hdr.xmin +
590 				(zoom_x_center - (zoom_x_dim / 2)),
591 				(hdr.ymax - (zoom_y_dim - 1)
592 				 - (zoom_y_center - (zoom_y_dim / 2))),
593 				hdr.xmin + (zoom_x_dim - 1)
594 				+ (zoom_x_center - (zoom_x_dim / 2)),
595 				(hdr.ymax
596 				 - (zoom_y_center - (zoom_y_dim / 2))));
597 
598 
599 		    }
600 		}
601 	    }
602 	}
603 	else if (rep.type == ExposeWindow || rep.type == ExposeRegion)
604 	{
605 	    xex = *((XExposeEvent *) & rep);
606 	    /*
607 	     * For icon exposure, just redraw whole thing - it's quick and
608 	     * much easier.
609 	     */
610 	    if (xex.window == iconwin)
611 		if ( bwflag != 2 )
612 		    XPixmapBitsPutZ(iconwin, 0, 0, iconrow, iconscan,
613 				    iconbuf, 0, GXcopy, AllPlanes);
614 		else
615 		    XBitmapBitsPut(iconwin, 0, 0, iconrow, iconscan,
616 				   iconbuf, 1, 0, 0, GXcopy, AllPlanes);
617 	    else if (zoomflg && (xex.window == zoomwin))
618 	    {
619 		resize_zoom_window();
620 		update_zoom();
621 	    }
622 	    else
623 	    {
624 		/*
625 		 * If window has been resized (bigger), don't bother redrawing
626 		 * the area outside the image.
627 		 */
628 		if (xex.y + xex.height >= nscan)
629 		    xex.height = nscan - xex.y;
630 		/*
631 		 * If bitmap, round beginning pixel to beginning of word
632 		 */
633 		if ( bwflag == 2 )
634 		{
635 		    xex.width += xex.x;	/* remember ending pixel */
636 		    xex.x = (xex.x / 16) * 16;	/* round down */
637 		    xex.width -= xex.x;	/* new width */
638 		}
639 		if (xex.x + xex.width >= nrow)
640 		    xex.width = nrow - xex.x;
641 		/*
642 		 * If no pixmap, do it the slow way.
643 		 */
644 		if (pix == 0)
645 		    if ( bwflag == 2 )
646 			for (i = xex.y; i < xex.y + xex.height; i++)
647 			    put_scanline( &buffer[i * nbyte + xex.x / 8],
648 					  xex.width, xex.x, i );
649 		    else
650 			for (i = xex.y; i < xex.y + xex.height; i++)
651 			    put_scanline( &buffer[i * nbyte + xex.x],
652 					  xex.width, xex.x, i );
653 		else
654 		{
655 		    /*
656 		     * Pixmaps exist, figure out how many slices are affected
657 		     * and which portion of each.
658 		     */
659 		    int             start = xex.y / pixscan,
660 		                    end = (xex.y + xex.height) / pixscan,
661 		                    y = xex.y,
662 		                    ytop;
663 		    register int    j;
664 
665 		    for (i = start; i <= end; i++)
666 		    {
667 			ytop = (i == end) ? xex.y + xex.height
668 			    : (i + 1) * pixscan;
669 			/*
670 			 * if this slice has a pixmap, blit it.  If not, do it
671 			 * slow from the buffer.
672 			 */
673 			if ((*pix)[i] != 0)
674 			    XPixmapPut(fbwin, xex.x, y - i * pixscan,
675 				       xex.x, y,
676 				       xex.width, ytop - y, (*pix)[i],
677 				       GXcopy, AllPlanes);
678 			else
679 			    if ( bwflag == 2 )
680 				for (j = y; j < ytop; j++)
681 				    put_scanline( &buffer[j * nbyte +
682 							  xex.x / 8],
683 						  xex.width, xex.x, j );
684 			    else
685 				for (j = y; j < ytop; j++)
686 				    put_scanline( &buffer[j * nbyte + xex.x],
687 						  xex.width, xex.x, j );
688 			y = ytop;
689 		    }
690 		}
691 	    }
692 	}
693 	else if ( rep.type != ButtonPressed )
694 	    fprintf(stderr, "%s: Event type %x?\n", hdr.cmd, rep.type);
695     }
696 
697     /*
698      * Window goes away when we do.
699      */
700     XDestroyWindow(fbwin);
701     if (zoomflg) XDestroyWindow(zoomwin);
702 }
703 
update_zoom()704 update_zoom()
705 {
706     unsigned char the_pix;
707     int i,j;
708     int zoomx, zoomy;
709     int x_bottom, x_top;
710     int y_bottom, y_top;
711     int zoom_x_corner, zoom_y_corner;
712 
713     x_bottom = zoom_x_center - (zoom_x_dim / 2);
714     x_top = x_bottom + zoom_x_dim - 1;
715     y_bottom = zoom_y_center - (zoom_y_dim / 2);
716     y_top = y_bottom + zoom_y_dim - 1;
717 
718     for (j=y_bottom; j<=y_top; j++)
719     {
720 	zoom_y_corner = (j - y_bottom) * zoomfact;
721 	if ((j < 0) || (j >= nscan))
722 	{
723 	    /* dump a blank line */
724 	    XPixSet( zoomwin, 0, zoom_y_corner, zoomfact * zoom_x_dim,
725 		    zoomfact, colmap[0].pixel );
726 	}
727 	else
728 	{
729 	    for ( i= x_bottom; i<=x_top; i++)
730 	    {
731 		zoom_x_corner = (i - x_bottom) * zoomfact;
732 		if ((i < 0) || (i >= nrow))
733 		{
734 		    /* dump a black pixel */
735 		    XPixSet( zoomwin, zoom_x_corner, zoom_y_corner,
736 			    zoomfact, zoomfact, colmap[0].pixel );
737 		}
738 		else
739 		{
740 		    if ( bwflag != 2 )
741 			the_pix = buffer[j * nbyte + i];
742 		    else
743 			the_pix = buffer[j * nbyte + i/8] & (1 << (i % 8)) ?
744 			    1 : 0;
745 		    XPixSet( zoomwin, zoom_x_corner, zoom_y_corner,
746 			    zoomfact, zoomfact, the_pix );
747 		}
748 	    }
749 	}
750     }
751 }
752 
753 /*
754  * Create a window with help from user.
755  */
create_window(width,height,window_geometry)756 create_window( width, height, window_geometry )
757 {
758     OpaqueFrame frame;
759     char geometry[30];
760     int zoomwidth, zoomheight;
761 
762     /*
763      * Now, make the window.
764      */
765     sprintf( geometry, "=%dx%d+0+0", nrow, nscan );
766     frame.bdrwidth = 3;
767     frame.border = WhitePixmap;
768     frame.background = BlackPixmap;
769     if ( (fbwin = XCreate( hdr.cmd, hdr.cmd, window_geometry, geometry,
770 			   &frame, nrow, nscan )) == NULL )
771     {
772 	fprintf( stderr, "%s: Window Create failed\n", hdr.cmd );
773 	exit( 1 );
774     }
775     XMapWindow( fbwin );
776     XGetHardwareColor( &colmap[0] );	/* Assume black! */
777     XPixSet( fbwin, 0, 0, width, height, colmap[0].pixel );
778     XSetResizeHint( fbwin, width, height, 1, 1 );
779     XSelectInput( fbwin, ButtonPressed|ButtonReleased|ExposeRegion );
780 
781     if (zoomflg)
782     {
783 	arrow_curs = XCreateCursor(arrow_width,arrow_height,arrow_bits,
784 			     arrow_mask_bits, arrow_x_hot, arrow_y_hot,
785 			     BlackPixel, WhitePixel, GXcopy);
786 	XDefineCursor(fbwin,arrow_curs);
787     }
788 
789     iconwin = XCreateWindow( RootWindow, 1, 1,
790 			     width / iconfact, height / iconfact, 0, 0, 0 );
791     XTileRelative( iconwin );
792     XSetIconWindow( fbwin, iconwin );
793     XSelectInput( iconwin, ButtonPressed|ButtonReleased|ExposeWindow );
794 
795     if (zoomflg)
796     {
797 	zoomwidth = zoomfact * zoom_x_dim;
798 	zoomheight = zoomfact * zoom_y_dim;
799 	sprintf( geometry, "=%dx%d+0+0", zoomwidth, zoomheight );
800 	frame.bdrwidth = 3;
801 	frame.border = WhitePixmap;
802 	frame.background = BlackPixmap;
803 	if ( (zoomwin = XCreate( "getx10 zoom", hdr.cmd, window_geometry,
804 				geometry,
805 				&frame, zoomwidth, zoomheight )) == NULL )
806 	{
807 	    fprintf( stderr, "%s: Zoom window Create failed\n", hdr.cmd );
808 	    exit( 1 );
809 	}
810 	resize_zoom_window();
811 	XMapWindow( zoomwin );
812 	XPixSet( zoomwin, 0, 0, zoomfact * zoom_x_dim, zoomfact * zoom_y_dim,
813 		colmap[0].pixel );
814 	XSetResizeHint( zoomwin, 0, 0, zoomfact, zoomfact );
815 	XSelectInput( zoomwin, ButtonPressed|ButtonReleased|ExposeRegion );
816 	XDefineCursor(zoomwin,arrow_curs);
817     }
818 }
819 
820 /*
821  * Map a scanline to 8 bits through the dither matrix.
822  *
823  * Inputs:
824  * 	rgb:		Pointers to buffers containing the red, green,
825  *			and blue color rows.
826  *	n:		Length of row.
827  *	s:		Skip between pixels in original image.
828  * 	y:		Y position of row (necessary for dither)
829  *	line:		Pointer to output buffer for dithered color data.
830  */
831 #define DMAP(v,x,y)	(modN[v]>dm16[x][y] ? divN[v] + 1 : divN[v])
832 
map_scanline(rgb,n,s,y,line)833 map_scanline( rgb, n, s, y, line )
834 unsigned char *rgb[3], *line;
835 {
836     register int i, col, row;
837 
838     if ( usemap )
839     {
840 	register unsigned char *r;
841 
842 	for ( r = rgb[0], i = 0; i < n; i++, r += s )
843 	    line[i] = colmap[*r].pixel;
844     }
845     else if ( !bwflag )
846     {
847 	register unsigned char *r, *g, *b;
848 	for ( row = y % 16, col = 0, i = 0, r = rgb[0], g = rgb[1], b = rgb[2];
849 	      i < n; i++, r+=s, g+=s, b+=s, col = ((col + 1) & 15) )
850 	    line[i] = colmap[DMAP(in_cmap[0][*r], col, row) +
851 			     DMAP(in_cmap[1][*g], col, row) * levels +
852 			     DMAP(in_cmap[2][*b], col, row) * levelsq].pixel;
853     }
854     else if ( bwflag == 1 )	/* gray scale display */
855     {
856 	register unsigned char *r;
857 
858 	for ( row = y % 16, col = 0, i = 0, r = rgb[0];
859 	      i < n; i++, r+=s, col = ((col + 1) & 15) )
860 	    line[i] = colmap[DMAP(*r, col, row)].pixel;
861     }
862     else			/* bitmap display */
863     {
864 	register unsigned short *l = (unsigned short *)line;
865 	register unsigned char * r;
866 
867 	for ( row = y % 16, col = 0, i = 0, r = rgb[0], *l = 0;
868 	      i < n; r+=s, col = ((col + 1) & 15) )
869 	{
870 	    *l |= (*r > dm16[col][row] ? 1 : 0) << (i % 16);
871 	    if ( (++i % 16) == 0 && i < n )
872 		*++l = 0;
873 	}
874     }
875 }
876 
put_scanline(scan,width,x,y)877 put_scanline( scan, width, x, y )
878 unsigned char *scan;
879 {
880     if ( bwflag != 2 )
881 	XPixmapBitsPutZ( fbwin, x, y, width, 1, scan, 0, GXcopy, AllPlanes );
882     else
883 	XBitmapBitsPut( fbwin, x, y, width, 1, scan, 1, 0, 0, GXcopy,
884 			AllPlanes );
885 }
886 
887 /*****************************************************************
888  * TAG( map_rgb_to_bw )
889  *
890  * Convert RGB to black and white through NTSC transform, but map
891  * RGB through a color map first.
892  * Inputs:
893  * 	red_row, green_row, blue_row:	Given RGB pixel data.
894  *	map:		Array[3] of pointers to pixel arrays,
895  *			representing color map.
896  *	rowlen:		Number of pixels in the rows.
897  * Outputs:
898  * 	bw_row:	    Output B&W data.  May coincide with one of the
899  *		    inputs.
900  * Algorithm:
901  * 	BW = .35*map[0][R] + .55*map[1][G] + .10*map[2][B]
902  */
map_rgb_to_bw(red_row,green_row,blue_row,bw_row,map,rowlen)903 map_rgb_to_bw( red_row, green_row, blue_row, bw_row, map, rowlen )
904 rle_pixel *red_row;
905 rle_pixel *green_row;
906 rle_pixel *blue_row;
907 rle_pixel *bw_row;
908 rle_pixel **map;
909 {
910     register int x, bw;
911 
912     for (x=0; x<rowlen; x++)
913     {
914 	/* 68000 won't store float > 127 into byte? */
915 	/* HP compiler blows it */
916 	bw = .35*map[0][red_row[x]] + .55*map[1][green_row[x]] +
917 	    .10*map[2][blue_row[x]];
918 	bw_row[x] = bw;
919     }
920 }
921 
922 
923 /*
924  * This routine builds the color map (for X window system, details may
925  * differ in your system.  Note particularly the two level color
926  * mapping:  X will give back 216 color map entries, but there is no
927  * guarantee they are sequential.  The dithering code above wants to
928  * compute a number in 0 - 215, so we must map from this to the X color
929  * map entry id.
930  */
931 /*
932  * Initialize the 8 bit color map.  Use gamma corrected map.
933  */
934 
init_color()935 init_color()
936 {
937     int i, rgbmap[256][3], bwmap[256];
938 #ifdef DEBUG
939     int j;
940 #endif
941 
942     if ( !bwflag )
943     {
944 	/*
945 	 * Figure out how many color map entries we can get
946 	 */
947 	if ( levels == 0 )
948 	    levels = 6;		/* default starting point */
949 	for ( ; levels >= 2; levels-- )
950 	    if ( levels*levels*levels < DisplayCells() )
951 		break;
952 	if ( levels < 2 )
953 	{
954 	    fprintf(stderr,
955 		    "%s: This display doesn't have enough color resolution.\
956 \nWill produce black and white image instead.\n", hdr.cmd );
957 	    bwflag = 1;
958 	    levels = 0;
959 	}
960 	/*
961 	 * levels is the maximum number we can get.  Now we have to try to
962 	 * actually acquire the color map entries.
963 	 */
964 	while ( levels >= 2 )
965 	{
966 	    levelsq = levels * levels;
967 	    levelsc = levelsq * levels;
968 
969 	    dithermap( levels, disp_gam, rgbmap, divN, modN, dm16 );
970 
971 	    /*
972 	     * Set up the color map entries.  We don't yet know the location
973 	     * in the map at which each will reside, so init it to 0.
974 	     */
975 	    for(i = 0; i < levelsc; i++) {
976 		colmap[i].pixel = 0;
977 		colmap[i].red = rgbmap[i][0] << 8;
978 		colmap[i].green = rgbmap[i][1] << 8;
979 		colmap[i].blue = rgbmap[i][2] << 8;
980 	    }
981 
982 	    /* Get a color map entry for each color.  Assume enough exist! */
983 	    for ( i = 0; i < levelsc; i++ )
984 	    {
985 		if ( XGetHardwareColor( &colmap[i] ) == 0 )
986 		    break;
987 	    }
988 
989 	    /* Check if the colors are available */
990 	    if ( i < levelsc )
991 	    {
992 		levels--;
993 		/* Free the colors already obtained */
994 		for ( i--; i >= 0; i-- )
995 		    XFreeColors( &colmap[i].pixel, 1, 0 );
996 		continue;
997 	    }
998 	    break;		/* succeeded */
999 	}
1000 	if ( levels < 2 && bwflag == 0 )
1001 	{
1002 	    fprintf( stderr,
1003 		     "%s: Sorry, not enough color map entries are available.\
1004 \nWill make black & white image instead.\n", hdr.cmd );
1005 	    bwflag = 1;
1006 	    levels = 0;
1007 	}
1008     }
1009 
1010     /* Get a B&W color map */
1011     if ( bwflag == 1 )
1012     {
1013 	/* Try for lots of levels, give up by factors of 2 until success */
1014 	if ( levels == 0 )
1015 	    if ( DisplayCells() > 16 )
1016 		levels = DisplayCells() / 2;
1017 	    else
1018 		levels = DisplayCells() - 4;	/* assume this many taken */
1019 	for ( ; levels > 1; (levels > 16) ? (levels /= 2) : (levels -= 1) )
1020 	{
1021 	    bwdithermap( levels, disp_gam, bwmap, divN, modN, dm16 );
1022 
1023 	    /*
1024 	     * Set up the color map entries.  We don't yet know the location
1025 	     * in the map at which each will reside, so init it to 0.
1026 	     */
1027 	    for ( i = 0; i < levels; i++ )
1028 	    {
1029 		colmap[i].pixel = 0;
1030 		colmap[i].red = bwmap[i] << 8;
1031 		colmap[i].green = colmap[i].red;
1032 		colmap[i].blue = colmap[i].red;
1033 	    }
1034 
1035 	    /* Get a color map entry for each color.  Assume enough exist! */
1036 	    for ( i = 0; i < levels; i++ )
1037 	    {
1038 		if ( XGetHardwareColor( &colmap[i] ) == 0 )
1039 		    break;
1040 	    }
1041 
1042 	    /* Check if the colors are available */
1043 	    if ( i < levels )
1044 	    {
1045 		/* Free the colors already obtained */
1046 		for ( i--; i >= 0; i-- )
1047 		    XFreeColors( &colmap[i].pixel, 1, 0 );
1048 		continue;
1049 	    }
1050 	    break;		/* succeeded */
1051 	}
1052 	if ( levels < 2 )
1053 	    bwflag = 2;		/* use 1 bit dithering */
1054 	else
1055 	    levelsc = levels;	/* remember full range */
1056     }
1057 
1058     /* If b&w 1-bit display, just use two colors */
1059     if ( bwflag == 2 )
1060     {
1061 	/* All we care about, really, is the magic square */
1062 	make_square( 255.0, divN, modN, dm16 );
1063 	levels = levelsc = 2;
1064     }
1065 
1066 #ifdef DEBUG
1067     printf( "Magic square for %d levels:\n", levels );
1068     for ( i = 0; i < 16; i++ )
1069     {
1070 	for ( j = 0; j < 16; j++ )
1071 	    printf( "%4d", dm16[i][j] );
1072 	printf( "\n" );
1073     }
1074     printf( "divN array:\n" );
1075     for ( i = 0; i < 256; i++ )
1076 	printf( "%4d%s", divN[i], i % 16 == 15 ? "\n" : "" );
1077     printf( "modN array:\n" );
1078     for ( i = 0; i < 256; i++ )
1079 	printf( "%4d%s", modN[i], i % 16 == 15 ? "\n" : "" );
1080 #endif
1081 }
1082 
1083 /*****************************************************************
1084  * TAG( load_x_map )
1085  *
1086  * Loads the color map from the input RLE file directly into X window
1087  * system.
1088  * Inputs:
1089  * 	in_cmap:	Global variable containing gamma corrected color
1090  * 			map from input file.
1091  * Outputs:
1092  * 	Loads color map (further gamma corrected for display) into
1093  * 	display.  If not enough display map entries are available,
1094  * 	prints a message and exits the program.
1095  * Assumptions:
1096  *	[None]
1097  * Algorithm:
1098  *	[None]
1099  */
load_x_map()1100 load_x_map()
1101 {
1102     int nmap = 0, i, j;
1103     char * mapcom;
1104     int gammamap[256];
1105 
1106     if ( (mapcom = rle_getcom( "color_map_length", &hdr )) )
1107 	nmap = atoi( mapcom );
1108     if ( nmap == 0 )
1109 	nmap = 1 << hdr.cmaplen;
1110 
1111     /* We won't be using in_cmap, so gamma correct it in place */
1112     for ( i = 0; i < 256; i++ )
1113 	gammamap[i] = (int)(0.5 + 255 * pow( i / 255.0, 1.0/disp_gam ));
1114 
1115     for ( i = 0; i < nmap; i++ )
1116 	for ( j = 0; j < 3; j++ )
1117 	    in_cmap[j][i] = gammamap[in_cmap[j][i]];
1118 
1119     /*
1120      * Set up the color map entries.  We don't yet know the location
1121      * in the map at which each will reside, so init it to 0.
1122      */
1123     for(i = 0; i < nmap; i++) {
1124 	colmap[i].pixel = 0;
1125 	colmap[i].red = in_cmap[0][i] << 8;
1126 	colmap[i].green = in_cmap[1][i] << 8;
1127 	colmap[i].blue = in_cmap[2][i] << 8;
1128 	if ( XGetHardwareColor( &colmap[i] ) == 0 )
1129 	{
1130 	    fprintf( stderr,
1131 	    "%s: Sorry, need at least %d color map entries, only got %d.\n\
1132 Will display image anyway.\n",
1133 		     hdr.cmd, nmap, i );
1134 	    break;
1135 	}
1136     }
1137     for ( ; i < 256; i++ )
1138 	colmap[i].pixel = 0;	/* for "undefined" colors */
1139 }
1140 
resize_zoom_window()1141 resize_zoom_window()
1142 {
1143     WindowInfo info;
1144     int xs, ys;
1145 
1146     XQueryWindow( zoomwin, &info );
1147 
1148     /* truncate to multiple of zoomfact */
1149     xs = zoomfact * ( info.width / zoomfact );
1150     ys = zoomfact * ( info.height / zoomfact );
1151 
1152     if ( xs != info.width || ys != info.height )
1153 	XChangeWindow( zoomwin, xs, ys );
1154 
1155     zoom_x_dim = xs / zoomfact;
1156     zoom_y_dim = ys / zoomfact;
1157 }
1158