1/*
2
3Copyright 1985, 1986, 1988, 1998  The Open Group
4
5Permission to use, copy, modify, distribute, and sell this software and its
6documentation for any purpose is hereby granted without fee, provided that
7the above copyright notice appear in all copies and that both that
8copyright notice and this permission notice appear in supporting
9documentation.
10
11The above copyright notice and this permission notice shall be included
12in all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
18OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20OTHER DEALINGS IN THE SOFTWARE.
21
22Except as contained in this notice, the name of The Open Group shall
23not be used in advertising or otherwise to promote the sale, use or
24other dealings in this Software without prior written authorization
25from The Open Group.
26
27*/
28
29/* xwud - marginally useful raster image undumper */
30
31#ifdef HAVE_CONFIG_H
32#include "config.h"
33#endif
34
35#include <X11/Xos.h>
36#include <X11/Xlib.h>
37#include <X11/Xutil.h>
38#include <X11/Xatom.h>
39#include <stdio.h>
40#include <X11/XWDFile.h>
41#define  XK_LATIN1
42#include <X11/keysymdef.h>
43#include <errno.h>
44#include <stdlib.h>
45
46static Atom wm_protocols;
47static Atom wm_delete_window;
48static int split;
49
50static char *progname;
51
52static Bool Read(char *ptr, int size, int nitems, FILE *stream);
53static void putImage(Display *dpy, Window image_win, GC gc,
54		     XImage *out_image, int x, int y, int w, int h);
55static void putScaledImage(Display *display, Drawable d, GC gc,
56			   XImage *src_image, int exp_x, int exp_y,
57			   unsigned int exp_width, unsigned int exp_height,
58			   unsigned int dest_width, unsigned dest_height);
59static void Latin1Upper(char *s);
60static void Extract_Plane(XImage *in_image, XImage *out_image, int plane);
61static int EffectiveSize(XVisualInfo *vinfo);
62static int VisualRank(int class);
63static int IsGray(Display *dpy, XStandardColormap *stdmap);
64static void Do_StdGray(Display *dpy, XStandardColormap *stdmap, int ncolors,
65		       XColor *colors, XImage *in_image, XImage *out_image);
66static void Do_StdCol(Display *dpy, XStandardColormap *stdmap, int ncolors,
67		      XColor *colors, XImage *in_image, XImage *out_image);
68static Colormap CopyColormapAndFree(Display *dpy, Colormap colormap);
69static void Do_Pseudo(Display *dpy, Colormap *colormap, int ncolors,
70		      XColor *colors, XImage *in_image, XImage *out_image);
71static void Do_Direct(Display *dpy, XWDFileHeader *header, Colormap *colormap,
72		      int ncolors, XColor *colors,
73		      XImage *in_image, XImage *out_image, XVisualInfo *vinfo);
74static unsigned int Image_Size(XImage *image);
75static void Error(const char *string) _X_NORETURN;
76static void _swapshort(char *bp, unsigned int n);
77static void _swaplong(char *bp, unsigned int n);
78static void DumpHeader(const XWDFileHeader *header, const char *win_name);
79
80static void _X_NORETURN _X_COLD
81usage(const char *errmsg)
82{
83    if (errmsg != NULL)
84        fprintf (stderr, "%s: %s\n\n", progname, errmsg);
85
86    fprintf(stderr, "usage: %s %s",
87            progname,
88            " [-in <file>] [-noclick] [-geometry <geom>] [-display <display>]\n"
89 "            [-new] [-std <maptype>] [-raw] [-vis <vis-type-or-id>]\n"
90 "            [-help] [-rv] [-plane <number>] [-fg <color>] [-bg <color>]\n"
91 "            [-scale] [-dumpheader] [-version]\n"
92        );
93    exit(1);
94}
95
96static Bool
97Read(char *ptr, int size, int nitems, FILE *stream)
98{
99    size *= nitems;
100    while (size) {
101	nitems = fread(ptr, 1, size, stream);
102	if (nitems <= 0)
103	    return False;
104	size -= nitems;
105	ptr += nitems;
106    }
107    return True;
108}
109
110int
111main(int argc, char *argv[])
112{
113    Display *dpy;
114    int screen;
115    register int i;
116    XImage in_image_struct;
117    XImage *in_image, *out_image;
118    XSetWindowAttributes attributes;
119    XVisualInfo vinfo, *vinfos;
120    long mask;
121    register char *buffer;
122    unsigned long swaptest = 1;
123    int count, stdcnt;
124    unsigned buffer_size;
125    int win_name_size;
126    int ncolors;
127    char *file_name = NULL;
128    char *win_name;
129    Bool inverse = False, rawbits = False, newmap = False;
130    Bool onclick = True;
131    Bool scale = False;
132    int plane = -1;
133    char *std = NULL;
134    char *vis = NULL;
135    char *display_name = NULL;
136    char *fgname = NULL;
137    char *bgname = NULL;
138    char *geom = NULL;
139    int gbits = 0;
140    XSizeHints hints;
141    XTextProperty textprop;
142    XClassHint class_hint;
143    XColor *colors = NULL, color, igncolor;
144    Window image_win;
145    Colormap colormap;
146    XEvent event;
147    register XExposeEvent *expose = (XExposeEvent *)&event;
148    GC gc;
149    XGCValues gc_val;
150    XWDFileHeader header;
151    XWDColor xwdcolor;
152    FILE *in_file = stdin;
153    char *map_name;
154    Atom map_prop;
155    XStandardColormap *stdmaps, *stdmap = NULL;
156    char c;
157    int win_width, win_height;
158    Bool dump_header = False;
159
160    progname = argv[0];
161
162    for (i = 1; i < argc; i++) {
163	if (strcmp(argv[i], "-bg") == 0) {
164	    if (++i >= argc) usage("-bg requires an argument");
165	    bgname = argv[i];
166	    continue;
167	}
168	if (strcmp(argv[i], "-display") == 0) {
169	    if (++i >= argc) usage("-display requires an argument");
170	    display_name = argv[i];
171	    continue;
172	}
173	if (strcmp(argv[i], "-dumpheader") == 0) {
174	    dump_header = True;
175	    continue;
176	}
177	if (strcmp(argv[i], "-fg") == 0) {
178	    if (++i >= argc) usage("-fg requires an argument");
179	    fgname = argv[i];
180	    continue;
181	}
182	if (strcmp(argv[i], "-geometry") == 0) {
183	    if (++i >= argc) usage("-geometry requires an argument");
184	    geom = argv[i];
185	    continue;
186	}
187	if ((strcmp(argv[i], "-help") == 0) ||
188            (strcmp(argv[i], "--help") == 0)) {
189	    usage(NULL);
190	}
191	if (strcmp(argv[i], "-in") == 0) {
192	    if (++i >= argc) usage("-in requires an argument");
193	    file_name = argv[i];
194	    continue;
195	}
196	if (strcmp(argv[i], "-inverse") == 0) { /* for compatibility */
197	    inverse = True;
198	    continue;
199	}
200	if (strcmp(argv[i], "-new") == 0) {
201	    newmap = True;
202	    if (std) usage("-new cannot be specified with -std");
203	    continue;
204	}
205	if (strcmp(argv[i], "-noclick") == 0) {
206	    onclick = False;
207	    continue;
208	}
209	if (strcmp(argv[i], "-plane") == 0) {
210	    if (++i >= argc) usage("-plane requires an argument");
211	    plane = atoi(argv[i]);
212	    continue;
213	}
214	if (strcmp(argv[i], "-raw") == 0) {
215	    rawbits = True;
216	    if (std) usage("-new cannot be specified with -std");
217	    continue;
218	}
219	if (strcmp(argv[i], "-rv") == 0) {
220	    inverse = True;
221	    continue;
222	}
223	if (strcmp(argv[i], "-scale") == 0) {
224	    scale = True;
225	    continue;
226	}
227	if (strcmp(argv[i], "-split") == 0) {
228	    split = True;
229	    continue;
230	}
231	if (strcmp(argv[i], "-std") == 0) {
232	    if (++i >= argc) usage("-std requires an argument");
233	    std = argv[i];
234	    if (newmap || rawbits)
235                usage("-std cannot be specified with -raw or -new");
236	    continue;
237	}
238	if (strcmp(argv[i], "-vis") == 0) {
239	    if (++i >= argc) usage("-vis requires an argument");
240	    vis = argv[i];
241	    continue;
242	}
243	if ((strcmp(argv[i], "-version") == 0) ||
244            (strcmp(argv[i], "--version") == 0)) {
245	    puts(PACKAGE_STRING);
246	    exit(0);
247	}
248	fprintf (stderr, "%s: unrecognized argument '%s'\n\n",
249		 progname, argv[i]);
250	usage(NULL);
251    }
252
253    if (file_name) {
254	in_file = fopen(file_name, "rb");
255	if (in_file == NULL)
256	    Error("Can't open input file as specified.");
257    }
258#ifdef WIN32
259    else
260	_setmode(fileno(in_file), _O_BINARY);
261#endif
262
263    dpy = XOpenDisplay(display_name);
264    if (dpy == NULL) {
265	fprintf(stderr, "%s:  unable to open display \"%s\"\n",
266		progname, XDisplayName(display_name));
267	exit(1);
268    }
269    screen = DefaultScreen(dpy);
270
271    /*
272     * Read in header information.
273     */
274    if(!Read((char *)&header, SIZEOF(XWDheader), 1, in_file))
275      Error("Unable to read dump file header.");
276
277    if (*(char *) &swaptest)
278	_swaplong((char *) &header, SIZEOF(XWDheader));
279
280    /* check to see if the dump file is in the proper format */
281    if (header.file_version != XWD_FILE_VERSION) {
282	fprintf(stderr,"xwud: XWD file format version mismatch.");
283	Error("exiting.");
284    }
285    if (header.header_size < SIZEOF(XWDheader)) {
286	fprintf(stderr,"xwud: XWD header size is too small.");
287	Error("exiting.");
288    }
289
290    /* alloc window name */
291    win_name_size = (header.header_size - SIZEOF(XWDheader));
292    if (win_name_size < 0)
293      Error("win_name_size");
294    if((win_name = malloc((unsigned) win_name_size + 6)) == NULL)
295      Error("Can't malloc window name storage.");
296    strcpy(win_name, "xwud: ");
297
298     /* read in window name */
299    if(!Read(win_name + 6, sizeof(char), win_name_size, in_file))
300      Error("Unable to read window name from dump file.");
301    (win_name + 6)[win_name_size - 1] = 0;
302
303    if (dump_header) {
304	DumpHeader(&header, win_name);
305	exit(0);
306    }
307
308    /* initialize the input image */
309
310    in_image = &in_image_struct;
311    in_image->depth = header.pixmap_depth;
312    in_image->format = header.pixmap_format;
313    in_image->xoffset = header.xoffset;
314    in_image->data = NULL;
315    in_image->width = header.pixmap_width;
316    in_image->height = header.pixmap_height;
317    in_image->bitmap_pad = header.bitmap_pad;
318    in_image->bytes_per_line = header.bytes_per_line;
319    in_image->byte_order = header.byte_order;
320    in_image->bitmap_unit = header.bitmap_unit;
321    in_image->bitmap_bit_order = header.bitmap_bit_order;
322    in_image->bits_per_pixel = header.bits_per_pixel;
323    in_image->red_mask = header.red_mask;
324    in_image->green_mask = header.green_mask;
325    in_image->blue_mask = header.blue_mask;
326    if (!XInitImage(in_image))
327	Error("Invalid input image header data.");
328
329    /* read in the color map buffer */
330    if((ncolors = header.ncolors)) {
331	colors = (XColor *)malloc((unsigned) ncolors * sizeof(XColor));
332	if (!colors)
333	    Error("Can't malloc color table");
334	for (i = 0; i < ncolors; i++) {
335	    if(!Read((char *) &xwdcolor, SIZEOF(XWDColor), 1, in_file))
336		Error("Unable to read color map from dump file.");
337	    colors[i].pixel = xwdcolor.pixel;
338	    colors[i].red = xwdcolor.red;
339	    colors[i].green = xwdcolor.green;
340	    colors[i].blue = xwdcolor.blue;
341	    colors[i].flags = xwdcolor.flags;
342	}
343	if (*(char *) &swaptest) {
344	    for (i = 0; i < ncolors; i++) {
345		_swaplong((char *) &colors[i].pixel, sizeof(long));
346		_swapshort((char *) &colors[i].red, 3 * sizeof(short));
347	    }
348	}
349    }
350    else
351	/* no color map exists, turn on the raw option */
352	rawbits = True;
353
354    /* alloc the pixel buffer */
355    buffer_size = Image_Size(in_image);
356    if((buffer = malloc(buffer_size)) == NULL)
357      Error("Can't malloc data buffer.");
358
359    /* read in the image data */
360    if (!Read(buffer, sizeof(char), (int)buffer_size, in_file))
361        Error("Unable to read pixmap from dump file.");
362
363     /* close the input file */
364    (void) fclose(in_file);
365
366    if (plane >= in_image->depth)
367	Error("plane number exceeds image depth");
368    if ((in_image->format == XYPixmap) && (plane >= 0)) {
369	buffer += in_image->bytes_per_line * in_image->height *
370		  (in_image->depth - (plane + 1));
371	in_image->depth = 1;
372	ncolors = 0;
373    }
374    if (in_image->bits_per_pixel == 1 && in_image->depth == 1) {
375	in_image->format = XYBitmap;
376	newmap = False;
377	rawbits = True;
378    }
379    in_image->data = buffer;
380
381    if (std) {
382	size_t map_name_len = strlen(std) + 9;
383
384	map_name = malloc(map_name_len);
385	if (map_name == NULL)
386	    Error("Can't malloc map name");
387	snprintf(map_name, map_name_len, "RGB_%s_MAP", std);
388	Latin1Upper(map_name);
389	map_prop = XInternAtom(dpy, map_name, True);
390	if (!map_prop || !XGetRGBColormaps(dpy, RootWindow(dpy, screen),
391					   &stdmaps, &stdcnt, map_prop))
392	    Error("specified standard colormap does not exist");
393    }
394    vinfo.screen = screen;
395    mask = VisualScreenMask;
396    if (vis)
397    {
398	char *vt = strdup(vis);
399	Latin1Upper(vt);
400	if (strcmp(vt, "STATICGRAY") == 0) {
401	    vinfo.class = StaticGray;
402	    mask |= VisualClassMask;
403	} else if (strcmp(vt, "GRAYSCALE") == 0) {
404	    vinfo.class = GrayScale;
405	    mask |= VisualClassMask;
406	} else if (strcmp(vt, "STATICCOLOR") == 0) {
407	    vinfo.class = StaticColor;
408	    mask |= VisualClassMask;
409	} else if (strcmp(vt, "PSEUDOCOLOR") == 0) {
410	    vinfo.class = PseudoColor;
411	    mask |= VisualClassMask;
412	} else if (strcmp(vt, "DIRECTCOLOR") == 0) {
413	    vinfo.class = DirectColor;
414	    mask |= VisualClassMask;
415	} else if (strcmp(vt, "TRUECOLOR") == 0) {
416	    vinfo.class = TrueColor;
417	    mask |= VisualClassMask;
418	} else if (strcmp(vt, "MATCH") == 0) {
419	    vinfo.class = header.visual_class;
420	    mask |= VisualClassMask;
421	} else if (strcmp(vt, "DEFAULT") == 0) {
422	    vinfo.visualid= XVisualIDFromVisual(DefaultVisual(dpy, screen));
423	    mask |= VisualIDMask;
424	} else {
425	    vinfo.visualid = 0;
426	    mask |= VisualIDMask;
427	    sscanf(vis, "0x%lx", &vinfo.visualid);
428	    if (!vinfo.visualid)
429	      sscanf(vis, "%lu", &vinfo.visualid);
430	    if (!vinfo.visualid)
431	      Error("invalid visual specifier");
432	}
433    }
434    if (rawbits && (in_image->depth > 1) && (plane < 0)) {
435	vinfo.depth = in_image->depth;
436	mask |= VisualDepthMask;
437    }
438    vinfos = XGetVisualInfo(dpy, mask, &vinfo, &count);
439    if (count == 0)
440	Error("no matching visual found");
441
442    /* find a workable visual */
443    if (std) {
444	stdmap = &stdmaps[0];
445	if (mask & VisualIDMask) {
446	    for (i = 0; i < stdcnt; i++) {
447		if (stdmaps[i].visualid == vinfo.visualid) {
448		    stdmap = &stdmaps[i];
449		    break;
450		}
451	    }
452	    if (stdmap->visualid != vinfo.visualid)
453		Error("no standard colormap matching specified visual");
454	}
455	for (i = 0; i < count; i++) {
456	    if (stdmap->visualid == vinfos[i].visualid) {
457		vinfo = vinfos[i];
458		break;
459	    }
460	}
461    } else if ((in_image->depth == 1) ||
462	       ((in_image->format == ZPixmap) && (plane >= 0)) ||
463	       rawbits) {
464	vinfo = vinfos[0];
465	if (!(mask & VisualIDMask)) {
466	    for (i = 0; i < count; i++) {
467		if ((vinfos[i].visualid ==
468		     XVisualIDFromVisual(DefaultVisual(dpy, screen))) &&
469		    (vinfos[i].depth == DefaultDepth(dpy, screen))) {
470		    vinfo = vinfos[i];
471		    break;
472		}
473	    }
474	}
475    } else {
476	/* get best visual */
477	vinfo = vinfos[0];
478	for (i = 0; i < count; i++) {
479	    int z1, z2;
480	    z2 = EffectiveSize(&vinfos[i]);
481	    if ((z2 >= ncolors) &&
482		(vinfos[i].depth == in_image->depth) &&
483		(vinfos[i].class == header.visual_class))
484	    {
485		vinfo = vinfos[i];
486		break;
487	    }
488	    z1 = EffectiveSize(&vinfo);
489	    if ((z2 > z1) ||
490		((z2 == z1) &&
491		 (VisualRank(vinfos[i].class) >= VisualRank(vinfo.class))))
492		vinfo = vinfos[i];
493	}
494	if ((newmap || (vinfo.visual != DefaultVisual(dpy, screen))) &&
495	    (vinfo.class != StaticGray) &&
496	    (vinfo.class != StaticColor) &&
497	    (vinfo.class == header.visual_class) &&
498	    (vinfo.depth == in_image->depth) &&
499	    ((vinfo.class == PseudoColor) ||
500	     (vinfo.class == GrayScale) ||
501	     ((vinfo.red_mask == header.red_mask) &&
502	      (vinfo.green_mask == header.green_mask) &&
503	      (vinfo.blue_mask == header.blue_mask)))) {
504	    rawbits = True;
505	    newmap = True;
506	}
507    }
508
509    /* get the appropriate colormap */
510    if (newmap && (vinfo.class & 1) &&
511	(vinfo.depth == in_image->depth) &&
512	(vinfo.class == header.visual_class) &&
513	(vinfo.colormap_size >= ncolors) &&
514	(vinfo.red_mask == header.red_mask) &&
515	(vinfo.green_mask == header.green_mask) &&
516	(vinfo.blue_mask == header.blue_mask)) {
517	colormap = XCreateColormap(dpy, RootWindow(dpy, screen), vinfo.visual,
518				   AllocAll);
519	if (ncolors) {
520	    for (i = 0; i < ncolors; i++)
521		colors[i].flags = DoRed|DoGreen|DoBlue;
522	    XStoreColors(dpy, colormap, colors, ncolors);
523	}
524    } else if (std) {
525	colormap = stdmap->colormap;
526    } else {
527	if (!newmap && (vinfo.visual == DefaultVisual(dpy, screen)))
528	    colormap = DefaultColormap(dpy, screen);
529	else
530	    colormap = XCreateColormap(dpy, RootWindow(dpy, screen),
531				       vinfo.visual, AllocNone);
532	newmap = False;
533    }
534
535    /* create the output image */
536    if ((in_image->format == ZPixmap) && (plane >= 0)) {
537	out_image = XCreateImage(dpy, vinfo.visual, 1,
538				 XYBitmap, 0, NULL,
539				 in_image->width, in_image->height,
540				 XBitmapPad(dpy), 0);
541	out_image->data = malloc(Image_Size(out_image));
542	if (out_image->data == NULL)
543	    Error("Can't malloc output image data");
544	Extract_Plane(in_image, out_image, plane);
545	ncolors = 0;
546    } else if (rawbits || newmap) {
547	out_image = in_image;
548    } else {
549	out_image = XCreateImage(dpy, vinfo.visual, vinfo.depth,
550				 (vinfo.depth == 1) ? XYBitmap :
551						      in_image->format,
552				 in_image->xoffset, NULL,
553				 in_image->width, in_image->height,
554				 XBitmapPad(dpy), 0);
555	out_image->data = malloc(Image_Size(out_image));
556	if (out_image->data == NULL)
557	    Error("Can't malloc output image data");
558	if (std) {
559	    if (!stdmap->green_max && !stdmap->blue_max && IsGray(dpy, stdmap))
560		Do_StdGray(dpy, stdmap, ncolors, colors, in_image, out_image);
561	    else
562		Do_StdCol(dpy, stdmap, ncolors, colors, in_image, out_image);
563	} else if ((header.visual_class == TrueColor) ||
564		   (header.visual_class == DirectColor))
565	    Do_Direct(dpy, &header, &colormap, ncolors, colors,
566		      in_image, out_image, &vinfo);
567	else
568	    Do_Pseudo(dpy, &colormap, ncolors, colors, in_image, out_image);
569    }
570
571    if (out_image->depth == 1) {
572	if (fgname &&
573	    XAllocNamedColor(dpy, colormap, fgname, &color, &igncolor))
574	    gc_val.foreground = color.pixel;
575	else if ((ncolors == 2) && XAllocColor(dpy, colormap, &colors[1]))
576	    gc_val.foreground = colors[1].pixel;
577	else
578	    gc_val.foreground = BlackPixel (dpy, screen);
579	if (bgname &&
580	    XAllocNamedColor(dpy, colormap, bgname, &color, &igncolor))
581	    gc_val.background = color.pixel;
582	else if ((ncolors == 2) && XAllocColor(dpy, colormap, &colors[0]))
583	    gc_val.background = colors[0].pixel;
584	else
585	    gc_val.background = WhitePixel (dpy, screen);
586	if (inverse) {
587	    unsigned long tmp;
588	    tmp = gc_val.foreground;
589	    gc_val.foreground = gc_val.background;
590	    gc_val.background = tmp;
591	}
592    } else {
593	gc_val.background = XGetPixel(out_image, 0, 0);
594	gc_val.foreground = 0;
595    }
596
597    attributes.background_pixel = gc_val.background;
598    attributes.border_pixel = gc_val.background;
599    if (scale)
600	attributes.bit_gravity = ForgetGravity;
601    else
602	attributes.bit_gravity = NorthWestGravity;
603    attributes.event_mask = ButtonPressMask|ButtonReleaseMask|KeyPressMask|
604			    ExposureMask;
605    if (scale)
606	attributes.event_mask |= StructureNotifyMask;
607    attributes.colormap = colormap;
608
609    hints.x = header.window_x;
610    hints.y = header.window_y;
611    hints.width = out_image->width;
612    hints.height = out_image->height;
613    if (geom)
614	gbits = XParseGeometry(geom, &hints.x, &hints.y,
615			       (unsigned int *)&hints.width,
616			       (unsigned int *)&hints.height);
617    hints.flags = ((gbits & (XValue|YValue)) ? USPosition : 0) |
618		  ((gbits & (HeightValue|WidthValue)) ? USSize : PSize);
619    if (!scale) {
620	hints.flags |= PMaxSize;
621	hints.max_width = (hints.width > out_image->width) ?
622	    hints.width : out_image->width;
623	hints.max_height = (hints.height > out_image->height) ?
624	    hints.height : out_image->height;
625    }
626    if ((gbits & XValue) && (gbits & XNegative))
627	hints.x += DisplayWidth(dpy, screen) - hints.width;
628    if ((gbits & YValue) && (gbits & YNegative))
629	hints.y += DisplayHeight(dpy, screen) - hints.height;
630
631    /* create the image window */
632    image_win = XCreateWindow(dpy, RootWindow(dpy, screen),
633			      hints.x, hints.y, hints.width, hints.height,
634			      0, vinfo.depth, InputOutput, vinfo.visual,
635			      CWBorderPixel|CWBackPixel|CWColormap|CWEventMask|CWBitGravity,
636			      &attributes);
637    win_width = hints.width;
638    win_height = hints.height;
639
640    /* Setup for ICCCM delete window. */
641    wm_protocols = XInternAtom(dpy, "WM_PROTOCOLS", False);
642    wm_delete_window = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
643    (void) XSetWMProtocols (dpy, image_win, &wm_delete_window, 1);
644
645    textprop.value = (unsigned char *) win_name;
646    textprop.encoding = XA_STRING;
647    textprop.format = 8;
648    textprop.nitems = strlen(win_name);
649    class_hint.res_name = (char *)NULL;
650    class_hint.res_class = "Xwud";
651    /* set standard properties */
652    XSetWMProperties(dpy, image_win, &textprop, (XTextProperty *)NULL,
653		     argv, argc, &hints, (XWMHints *)NULL, &class_hint);
654
655    /* map the image window */
656    XMapWindow(dpy, image_win);
657
658    gc = XCreateGC (dpy, image_win, GCForeground|GCBackground, &gc_val);
659
660    while (1) {
661	/* wait on mouse input event to terminate */
662	XNextEvent(dpy, &event);
663	switch(event.type) {
664	case ClientMessage:
665	  if (event.xclient.message_type == wm_protocols &&
666	      event.xclient.data.l[0] == wm_delete_window)  {
667	    XCloseDisplay(dpy);
668	    exit(0);		/* ICCCM delete window */
669	  }
670	    break;
671	case ButtonPress:
672	    break;
673	case ButtonRelease:
674	    if (onclick) {
675		XCloseDisplay(dpy);
676		exit(0);
677	    }
678	    break;
679	case KeyPress:
680	    i = XLookupString(&event.xkey, &c, 1, NULL, NULL);
681	    if ((i == 1) && ((c == 'q') || (c == 'Q') || (c == '\03'))) {
682		XCloseDisplay(dpy);
683		exit(0);
684	    }
685	    break;
686	case ConfigureNotify:
687	    win_width = event.xconfigure.width;
688	    win_height = event.xconfigure.height;
689	    break;
690	case Expose:
691	    if (scale)
692		putScaledImage(dpy, image_win, gc, out_image,
693			       expose->x, expose->y,
694			       expose->width, expose->height,
695			       win_width, win_height);
696	    else if ((expose->x < out_image->width) &&
697		     (expose->y < out_image->height)) {
698		if ((out_image->width - expose->x) < expose->width)
699		    expose->width = out_image->width - expose->x;
700		if ((out_image->height - expose->y) < expose->height)
701		    expose->height = out_image->height - expose->y;
702		putImage(dpy, image_win, gc, out_image,
703			  expose->x, expose->y,
704			  expose->width, expose->height);
705	    }
706	    break;
707	}
708    }
709    exit(0);
710}
711
712static void
713putImage(Display *dpy, Window image_win, GC gc, XImage *out_image,
714	 int x, int y, int w, int h)
715{
716#define SPLIT_SIZE  100
717    int	t_x, t_y, t_w, t_h;
718    if (split) {
719    	for (t_y = y; t_y < y + h; t_y += t_h) {
720    	    t_h = SPLIT_SIZE;
721    	    if (t_y + t_h > y + h)
722	    	t_h = y + h - t_y;
723    	    for (t_x = x; t_x < x + w; t_x += t_w) {
724	    	t_w = SPLIT_SIZE;
725	    	if (t_x + t_w > x + w)
726	    	    t_w = x + w - t_x;
727	    	XPutImage(dpy, image_win, gc, out_image,
728		      	  t_x, t_y, t_x, t_y, t_w, t_h);
729    	    }
730    	}
731    } else {
732	XPutImage (dpy, image_win, gc, out_image, x, y, x, y, w, h);
733    }
734}
735
736typedef short Position;
737typedef unsigned short Dimension;
738typedef unsigned long Pixel;
739
740#define roundint(x)                   (int)((x) + 0.5)
741
742typedef struct {
743  Position *x, *y;
744  Dimension *width, *height;
745} Table;
746
747static void
748putScaledImage(Display *display, Drawable d, GC gc, XImage *src_image,
749	       int exp_x, int exp_y,
750	       unsigned int exp_width, unsigned int exp_height,
751	       unsigned int dest_width, unsigned dest_height)
752{
753    XImage *dest_image;
754    Position x, y, min_y, max_y, exp_max_y, src_x, src_max_x, src_y;
755    Dimension w, h, strip_height;
756    Table table;
757    Pixel pixel;
758    double ratio_x, ratio_y;
759    Bool fast8;
760
761    if (dest_width == src_image->width && dest_height == src_image->height) {
762	/* same for x and y, just send it out */
763	XPutImage(display, d, gc, src_image, exp_x, exp_y,
764		  exp_x, exp_y, exp_width, exp_height);
765	return;
766    }
767
768    ratio_x = (double)dest_width / (double)src_image->width;
769    ratio_y = (double)dest_height / (double)src_image->height;
770
771    src_x = exp_x / ratio_x;
772    if (src_x >= src_image->width)
773	src_x = src_image->width - 1;
774    src_y = exp_y / ratio_y;
775    if (src_y >= src_image->height)
776	src_y = src_image->height - 1;
777    exp_max_y = exp_y + exp_height;
778    src_max_x = roundint((exp_x + exp_width) / ratio_x) + 1;
779    if (src_max_x > src_image->width)
780	src_max_x = src_image->width;
781
782    strip_height = 65536 / roundint(ratio_x * src_image->bytes_per_line);
783    if (strip_height == 0)
784	strip_height = 1;
785    if (strip_height > exp_height)
786	strip_height = exp_height;
787
788    h = strip_height + roundint(ratio_y);
789    dest_image = XCreateImage(display,
790			      DefaultVisualOfScreen(
791					     DefaultScreenOfDisplay(display)),
792			      src_image->depth, src_image->format,
793			      0, NULL,
794			      dest_width, h,
795			      src_image->bitmap_pad, 0);
796    dest_image->data = malloc(dest_image->bytes_per_line * h);
797    if (dest_image->data == NULL)
798        Error("Can't malloc scaled image data");
799    fast8 = (src_image->depth == 8 && src_image->bits_per_pixel == 8 &&
800	     dest_image->bits_per_pixel == 8 && src_image->format == ZPixmap);
801
802    table.x = (Position *) malloc(sizeof(Position) * (src_image->width + 1));
803    if (table.x == NULL)
804        Error("Can't malloc scaled image X table");
805    table.y = (Position *) malloc(sizeof(Position) * (src_image->height + 1));
806    if (table.y == NULL)
807        Error("Can't malloc scaled image Y table");
808    table.width = (Dimension *) malloc(sizeof(Dimension) * src_image->width);
809    if (table.width == NULL)
810        Error("Can't malloc scaled image width table");
811    table.height = (Dimension *) malloc(sizeof(Dimension)*src_image->height);
812    if (table.height == NULL)
813        Error("Can't malloc scaled image height table");
814
815    table.x[0] = 0;
816    for (x = 1; x <= src_image->width; x++) {
817	table.x[x] = roundint(ratio_x * x);
818	table.width[x - 1] = table.x[x] - table.x[x - 1];
819    }
820
821    table.y[0] = 0;
822    for (y = 1; y <= src_image->height; y++) {
823	table.y[y] = roundint(ratio_y * y);
824	table.height[y - 1] = table.y[y] - table.y[y - 1];
825    }
826
827    for (min_y = table.y[src_y]; min_y < exp_max_y; min_y = table.y[y]) {
828	max_y = min_y + strip_height;
829	if (max_y > exp_max_y) {
830	    strip_height = exp_max_y - min_y;
831	    max_y = exp_max_y;
832	}
833	for (y = src_y; table.y[y] < max_y; y++) {
834	    if (table.y[y] < min_y)
835		continue;
836	    if (fast8) {
837		for (x = src_x; x < src_max_x; x++) {
838		    pixel = ((unsigned char *)src_image->data)
839			[y * src_image->bytes_per_line + x];
840		    for (h = 0; h < table.height[y]; h++) {
841			memset(dest_image->data +
842			       (table.y[y] + h - min_y) *
843			       dest_image->bytes_per_line + table.x[x],
844			       pixel, table.width[x]);
845		    }
846		}
847	    } else {
848		for (x = src_x; x < src_max_x; x++) {
849		    pixel = XGetPixel(src_image, x, y);
850		    for (h = 0; h < table.height[y]; h++) {
851			for (w = 0; w < table.width[x]; w++)
852			    XPutPixel(dest_image,
853				      table.x[x] + w,
854				      table.y[y] + h - min_y,
855				      pixel);
856		    }
857		}
858	    }
859	}
860	XPutImage(display, d, gc, dest_image, exp_x, 0,
861		  exp_x, min_y, exp_width, table.y[y] - min_y);
862	if (y >= src_image->height)
863	    break;
864    }
865
866    free(table.x);
867    free(table.y);
868    free(table.width);
869    free(table.height);
870
871    XDestroyImage(dest_image);
872}
873
874static void
875Latin1Upper(char *s)
876{
877    unsigned char *str = (unsigned char *)s;
878    unsigned char c;
879
880    for (; (c = *str); str++)
881    {
882	if ((c >= XK_a) && (c <= XK_z))
883	    *str = c - (XK_a - XK_A);
884	else if ((c >= XK_agrave) && (c <= XK_odiaeresis))
885	    *str = c - (XK_agrave - XK_Agrave);
886	else if ((c >= XK_oslash) && (c <= XK_thorn))
887	    *str = c - (XK_oslash - XK_Ooblique);
888    }
889}
890
891static void
892Extract_Plane(XImage *in_image, XImage *out_image, int plane)
893{
894    register int x, y;
895
896    for (y = 0; y < in_image->height; y++)
897	for (x = 0; x < in_image->width; x++)
898	    XPutPixel(out_image, x, y,
899		      (XGetPixel(in_image, x, y) >> plane) & 1);
900}
901
902static int
903EffectiveSize(XVisualInfo *vinfo)
904{
905    if ((vinfo->class == DirectColor) || (vinfo->class == TrueColor))
906	return (vinfo->red_mask | vinfo->green_mask | vinfo->blue_mask) + 1;
907    else
908	return vinfo->colormap_size;
909}
910
911static int
912VisualRank(int class)
913{
914    switch (class) {
915    case PseudoColor:
916	return 5;
917    case TrueColor:
918	return 4;
919    case DirectColor:
920	return 3;
921    case StaticColor:
922	return 2;
923    case GrayScale:
924	return 1;
925    case StaticGray:
926	return 0;
927    }
928    /* NOTREACHED */
929    return -1;
930}
931
932static int
933IsGray(Display *dpy, XStandardColormap *stdmap)
934{
935    XColor color;
936
937    color.pixel = stdmap->base_pixel + (stdmap->red_max * stdmap->red_mult);
938    XQueryColor(dpy, stdmap->colormap, &color);
939    return (color.green || color.blue);
940}
941
942static void
943Do_StdGray(Display *dpy, XStandardColormap *stdmap,
944	   int ncolors, XColor *colors, XImage *in_image, XImage *out_image)
945{
946    register int i, x, y;
947    register XColor *color;
948    unsigned lim;
949
950    lim = stdmap->red_max + 1;
951    for (i = 0, color = colors; i < ncolors; i++, color++)
952	color->pixel = stdmap->base_pixel +
953		       (((((int)(30L * color->red +
954			         59L * color->green +
955			         11L * color->blue) / 100)
956			  * lim) >> 16) * stdmap->red_mult);
957    for (y = 0; y < in_image->height; y++) {
958	for (x = 0; x < in_image->width; x++) {
959	    XPutPixel(out_image, x, y,
960		      colors[XGetPixel(in_image, x, y)].pixel);
961	}
962    }
963}
964
965#define MapVal(val,lim,mult) ((((val * lim) + 32768) / 65535) * mult)
966
967static void
968Do_StdCol(Display *dpy, XStandardColormap *stdmap,
969	  int ncolors, XColor *colors, XImage *in_image, XImage *out_image)
970{
971    register int i, x, y;
972    register XColor *color;
973    unsigned limr, limg, limb;
974
975    limr = stdmap->red_max;
976    limg = stdmap->green_max;
977    limb = stdmap->blue_max;
978    for (i = 0, color = colors; i < ncolors; i++, color++)
979	color->pixel = stdmap->base_pixel +
980		       MapVal(color->red, limr, stdmap->red_mult) +
981		       MapVal(color->green, limg, stdmap->green_mult) +
982		       MapVal(color->blue, limb, stdmap->blue_mult);
983    for (y = 0; y < in_image->height; y++) {
984	for (x = 0; x < in_image->width; x++) {
985	    XPutPixel(out_image, x, y,
986		      colors[XGetPixel(in_image, x, y)].pixel);
987	}
988    }
989}
990
991static Colormap
992CopyColormapAndFree(Display *dpy, Colormap colormap)
993{
994    if (colormap == DefaultColormap(dpy, DefaultScreen(dpy)))
995	return XCopyColormapAndFree(dpy, colormap);
996    Error("Visual type is not large enough to hold all colors of the image.");
997    /*NOTREACHED*/
998    return (Colormap)0;
999}
1000
1001static void
1002Do_Pseudo(Display *dpy, Colormap *colormap,
1003	  int ncolors, XColor *colors, XImage *in_image, XImage *out_image)
1004{
1005    register int i, x, y;
1006    register XColor *color;
1007
1008    for (i = 0; i < ncolors; i++)
1009	colors[i].flags = 0;
1010    for (y = 0; y < in_image->height; y++) {
1011	for (x = 0; x < in_image->width; x++) {
1012	    color = &colors[XGetPixel(in_image, x, y)];
1013	    if (!color->flags) {
1014		color->flags = DoRed | DoGreen | DoBlue;
1015		if (!XAllocColor(dpy, *colormap, color)) {
1016		    *colormap = CopyColormapAndFree(dpy, *colormap);
1017		    XAllocColor(dpy, *colormap, color);
1018		}
1019	    }
1020	    XPutPixel(out_image, x, y, color->pixel);
1021	}
1022    }
1023}
1024
1025static void
1026Do_Direct(Display *dpy, XWDFileHeader *header, Colormap *colormap,
1027	  int ncolors, XColor *colors, XImage *in_image, XImage *out_image,
1028          XVisualInfo *vinfo)
1029{
1030    register int x, y;
1031    XColor color;
1032    unsigned long rmask, gmask, bmask;
1033    unsigned long ormask, ogmask, obmask;
1034    unsigned long  rshift = 0, gshift = 0, bshift = 0;
1035    unsigned long  orshift = 0, ogshift = 0, obshift = 0;
1036    int i;
1037    unsigned long pix, xpix;
1038    unsigned long *pixels, *rpixels;
1039
1040    rmask = header->red_mask;
1041    while (!(rmask & 1)) {
1042	rmask >>= 1;
1043	rshift++;
1044    }
1045    gmask = header->green_mask;
1046    while (!(gmask & 1)) {
1047	gmask >>= 1;
1048	gshift++;
1049    }
1050    bmask = header->blue_mask;
1051    while (!(bmask & 1)) {
1052	bmask >>= 1;
1053	bshift++;
1054    }
1055    if (in_image->depth <= 12) {
1056	pix = 1 << in_image->depth;
1057	pixels = (unsigned long *)malloc(sizeof(unsigned long) * pix);
1058	if (pixels == NULL)
1059	    Error("Unable to allocate memory for pixel conversion");
1060	for (i = 0; i < pix; i++)
1061	    pixels[i] = ~0L;
1062	color.flags = DoRed | DoGreen | DoBlue;
1063	for (y = 0; y < in_image->height; y++) {
1064	    for (x = 0; x < in_image->width; x++) {
1065		pix = XGetPixel(in_image, x, y);
1066		if ((color.pixel = pixels[pix]) == ~0L) {
1067		    color.red = (pix >> rshift) & rmask;
1068		    color.green = (pix >> gshift) & gmask;
1069		    color.blue = (pix >> bshift) & bmask;
1070		    if (ncolors) {
1071			color.red = colors[color.red].red;
1072			color.green = colors[color.green].green;
1073			color.blue = colors[color.blue].blue;
1074		    } else {
1075			color.red = (((unsigned long)color.red * 65535) /
1076				     rmask);
1077			color.green = (((unsigned long)color.green * 65535) /
1078				       gmask);
1079			color.blue = (((unsigned long)color.blue * 65535) /
1080				      bmask);
1081		    }
1082		    if (!XAllocColor(dpy, *colormap, &color)) {
1083			*colormap = CopyColormapAndFree(dpy, *colormap);
1084			XAllocColor(dpy, *colormap, &color);
1085		    }
1086		    pixels[pix] = color.pixel;
1087		}
1088		XPutPixel(out_image, x, y, color.pixel);
1089	    }
1090	}
1091	free(pixels);
1092    } else if (header->visual_class == TrueColor &&
1093	       vinfo->class == TrueColor) {
1094	ormask = vinfo->red_mask;
1095	while (!(ormask & 1)) {
1096	    ormask >>= 1;
1097	    orshift++;
1098	}
1099	ogmask = vinfo->green_mask;
1100	while (!(ogmask & 1)) {
1101	    ogmask >>= 1;
1102	    ogshift++;
1103	}
1104	obmask = vinfo->blue_mask;
1105	while (!(obmask & 1)) {
1106	    obmask >>= 1;
1107	    obshift++;
1108	}
1109	for (y = 0; y < in_image->height; y++) {
1110	    for (x = 0; x < in_image->width; x++) {
1111		pix = XGetPixel(in_image, x, y);
1112		xpix = (((((pix >> rshift) & rmask) * 65535 / rmask)
1113			 * ormask / 65535) << orshift) |
1114		       (((((pix >> gshift) & gmask) * 65535 / gmask)
1115			 * ogmask / 65535) << ogshift) |
1116		       (((((pix >> bshift) & bmask) * 65535 / bmask)
1117			 * obmask / 65535) << obshift);
1118		XPutPixel(out_image, x, y, xpix);
1119	    }
1120	}
1121    } else {
1122	if (header->visual_class == TrueColor)
1123	    ncolors = 0;
1124	pix = 1 << 12;
1125	pixels = (unsigned long *)malloc(sizeof(unsigned long) * pix);
1126	rpixels = (unsigned long *)malloc(sizeof(unsigned long) * pix);
1127	if ((pixels == NULL) || (rpixels == NULL))
1128	    Error("Unable to allocate memory for pixel conversion");
1129	for (i = 0; i < pix; i++) {
1130	    pixels[i] = ~0L;
1131	    rpixels[i] = ~0L;
1132	}
1133	color.flags = DoRed | DoGreen | DoBlue;
1134	for (y = 0; y < in_image->height; y++) {
1135	    for (x = 0; x < in_image->width; x++) {
1136		pix = XGetPixel(in_image, x, y);
1137		xpix = ((pix >> 12) ^ pix) & ((1 << 12) - 1);
1138		if (((color.pixel = pixels[xpix]) == ~0L) ||
1139		    (rpixels[xpix] != pix)) {
1140		    color.red = (pix >> rshift) & rmask;
1141		    color.green = (pix >> gshift) & gmask;
1142		    color.blue = (pix >> bshift) & bmask;
1143		    if (ncolors) {
1144			color.red = colors[color.red].red;
1145			color.green = colors[color.green].green;
1146			color.blue = colors[color.blue].blue;
1147		    } else {
1148			color.red = (((unsigned long)color.red * 65535) /
1149				     rmask);
1150			color.green = (((unsigned long)color.green * 65535) /
1151				       gmask);
1152			color.blue = (((unsigned long)color.blue * 65535) /
1153				      bmask);
1154		    }
1155		    if (!XAllocColor(dpy, *colormap, &color)) {
1156			*colormap = CopyColormapAndFree(dpy, *colormap);
1157			XAllocColor(dpy, *colormap, &color);
1158		    }
1159		    pixels[xpix] = color.pixel;
1160		    rpixels[xpix] = pix;
1161		}
1162		XPutPixel(out_image, x, y, color.pixel);
1163	    }
1164	}
1165	free(pixels);
1166	free(rpixels);
1167    }
1168}
1169
1170static unsigned int
1171Image_Size(XImage *image)
1172{
1173    if (image->format != ZPixmap)
1174      return(image->bytes_per_line * image->height * image->depth);
1175
1176    return((unsigned)image->bytes_per_line * image->height);
1177}
1178
1179static void
1180Error(const char *string)
1181{
1182	fprintf(stderr, "xwud: Error => %s\n", string);
1183	if (errno != 0) {
1184		perror("xwud");
1185		fprintf(stderr, "\n");
1186	}
1187	exit(1);
1188}
1189
1190static void
1191_swapshort(char *bp, unsigned int n)
1192{
1193    register char c;
1194    register char *ep = bp + n;
1195
1196    while (bp < ep) {
1197	c = *bp;
1198	*bp = *(bp + 1);
1199	bp++;
1200	*bp++ = c;
1201    }
1202}
1203
1204static void
1205_swaplong(char *bp, unsigned int n)
1206{
1207    register char c;
1208    register char *ep = bp + n;
1209    register char *sp;
1210
1211    while (bp < ep) {
1212	sp = bp + 3;
1213	c = *sp;
1214	*sp = *bp;
1215	*bp++ = c;
1216	sp = bp + 1;
1217	c = *sp;
1218	*sp = *bp;
1219	*bp++ = c;
1220	bp += 2;
1221    }
1222}
1223
1224static void
1225DumpHeader(const XWDFileHeader *header, const char *win_name)
1226{
1227	printf("window name:        %s\n", win_name);
1228	printf("sizeof(XWDheader):  %d\n", (int)sizeof(*header));
1229	printf("header size:        %d\n", (int)header->header_size);
1230	printf("file version:       %d\n", (int)header->file_version);
1231	printf("pixmap format:      %d\n", (int)header->pixmap_format);
1232	printf("pixmap depth:       %d\n", (int)header->pixmap_depth);
1233	printf("pixmap width:       %d\n", (int)header->pixmap_width);
1234	printf("pixmap height:      %d\n", (int)header->pixmap_height);
1235	printf("x offset:           %d\n", (int)header->xoffset);
1236	printf("byte order:         %d\n", (int)header->byte_order);
1237	printf("bitmap unit:        %d\n", (int)header->bitmap_unit);
1238	printf("bitmap bit order:   %d\n", (int)header->bitmap_bit_order);
1239	printf("bitmap pad:         %d\n", (int)header->bitmap_pad);
1240	printf("bits per pixel:     %d\n", (int)header->bits_per_pixel);
1241	printf("bytes per line:     %d\n", (int)header->bytes_per_line);
1242	printf("visual class:       %d\n", (int)header->visual_class);
1243	printf("red mask:           %d\n", (int)header->red_mask);
1244	printf("green mask:         %d\n", (int)header->green_mask);
1245	printf("blue mask:          %d\n", (int)header->blue_mask);
1246	printf("bits per rgb:       %d\n", (int)header->bits_per_rgb);
1247	printf("colormap entries:   %d\n", (int)header->colormap_entries);
1248	printf("num colors:         %d\n", (int)header->ncolors);
1249	printf("window width:       %d\n", (int)header->window_width);
1250	printf("window height:      %d\n", (int)header->window_height);
1251	printf("window x:           %d\n", (int)header->window_x);
1252	printf("window y:           %d\n", (int)header->window_y);
1253	printf("border width:       %d\n", (int)header->window_bdrwidth);
1254}
1255
1256