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