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