1 /* xwd.c:
2  *
3  * XWD file reader.  unfortunately the bozo who thought up this format didn't
4  * define anything at all that we can use as an identifier or even to tell
5  * what kind of machine dumped the format.  what this does is read the
6  * header and look at several fields to decide if this *might* be an XWD
7  * file and if it is what byte order machine wrote it.
8  *
9  * jim frost 07.24.90
10  *
11  * Copyright 1990 Jim Frost.  See included file "copyright.h" for complete
12  * copyright information.
13  */
14 
15 #include "copyright.h"
16 #include "xloadimage.h"
17 #include "xwd.h"
18 
19 /* SUPPRESS 558 */
20 
21 /* this reads the header and does the magic to determine if it is indeed
22  * an XWD file.
23  */
24 
isXWD(name,zf,header,verbose)25 static int isXWD(name, zf, header, verbose)
26      char      *name;
27      ZFILE     *zf;
28      XWDHeader *header;
29      int        verbose;
30 { GenericXWDHeader  gh;
31   int               a;
32 
33   if (zread(zf, (byte *)&gh, sizeof(GenericXWDHeader)) != sizeof(GenericXWDHeader))
34     return(0);
35 
36   /* first try -- see if XWD version number matches in either MSB or LSB order
37    */
38 
39   if (memToVal(gh.file_version, 4) != XWD_VERSION)
40     return(0);
41 
42   /* convert fields to fill out header.  things we don't care about
43    * are commented out.
44    */
45 
46   header->header_size= memToVal(gh.header_size, 4);
47   header->file_version= memToVal(gh.file_version, 4);
48   header->pixmap_format= memToVal(gh.pixmap_format, 4);
49   header->pixmap_depth= memToVal(gh.pixmap_depth, 4);
50   header->pixmap_width= memToVal(gh.pixmap_width, 4);
51   header->pixmap_height= memToVal(gh.pixmap_height, 4);
52   header->xoffset= memToVal(gh.xoffset, 4);
53   header->byte_order= memToVal(gh.byte_order, 4);
54   header->bitmap_unit= memToVal(gh.bitmap_unit, 4);
55   header->bitmap_bit_order= memToVal(gh.bitmap_bit_order, 4);
56   header->bitmap_pad= memToVal(gh.bitmap_pad, 4);
57   header->bits_per_pixel= memToVal(gh.bits_per_pixel, 4);
58   header->bytes_per_line= memToVal(gh.bytes_per_line, 4);
59   header->visual_class= memToVal(gh.visual_class, 4);
60 /*header->red_mask= memToVal(gh.red_mask, 4);*/
61 /*header->green_mask= memToVal(gh.green_mask, 4);*/
62 /*header->blue_mask= memToVal(gh.blue_mask, 4);*/
63 /*header->bits_per_rgb= memToVal(gh.bits_per_rgb, 4);*/
64   header->colormap_entries= memToVal(gh.colormap_entries, 4);
65   header->ncolors= memToVal(gh.ncolors, 4);
66 /*header->window_width= memToVal(gh.window_width, 4);*/
67 /*header->window_height= memToVal(gh.window_height, 4);*/
68 /*header->window_x= memToVal(gh.window_x, 4);*/
69 /*header->window_y= memToVal(gh.window_y, 4);*/
70 /*header->window_bdrwidth= memToVal(gh.window_bdrwidth, 4);*/
71 
72   /* if header size isn't either 100 or 104 bytes, this isn't an XWD file
73    */
74 
75   if (header->header_size < sizeof(GenericXWDHeader))
76     return(0);
77 
78   for (a= header->header_size - sizeof(GenericXWDHeader); a; a--)
79     zgetc(zf);
80 
81   /* look at a variety of the XImage fields to see if they are sane.  if
82    * they are, this passes our tests.
83    */
84 
85   switch (header->pixmap_format) {
86   case XYBitmap:
87   case XYPixmap:
88   case ZPixmap:
89     break;
90   default:
91     return(0);
92   }
93 
94   switch (header->visual_class) {
95   case StaticGray:
96   case GrayScale:
97   case StaticColor:
98   case PseudoColor:
99 
100     /* the following are unsupported but recognized
101      */
102 
103   case TrueColor:
104   case DirectColor:
105     break;
106   default:
107     return(0);
108   }
109 
110   if (verbose) {
111     printf("%s is a %dx%d XWD image in ",
112 	   name, header->pixmap_width, header->pixmap_height);
113     switch (header->pixmap_format) {
114     case XYBitmap:
115       printf("XYBitmap");
116       break;
117     case XYPixmap:
118       printf("%d bit XYPixmap", header->pixmap_depth);
119       break;
120     case ZPixmap:
121       printf("%d bit ZPixmap", header->pixmap_depth);
122       break;
123     }
124     printf(" format\n");
125   }
126 
127   /* if it got this far, we're pretty damned certain we've got the right
128    * file type and know what order it's in.
129    */
130 
131   znocache(zf);
132   return(1);
133 }
134 
xwdIdent(fullname,name)135 int xwdIdent(fullname, name)
136      char *fullname, *name;
137 { ZFILE     *zf;
138   XWDHeader  header;
139   int ret;
140 
141   if (! (zf= zopen(fullname)))
142     return(0);
143   ret= isXWD(name, zf, &header, 1);
144   zclose(zf);
145   return(ret);
146 }
147 
loadXYBitmap(fullname,zf,header)148 static Image *loadXYBitmap(fullname, zf, header)
149      char *fullname;
150      ZFILE     *zf;
151      XWDHeader  header;
152 { Image *image;
153   int    dlinelen;       /* length of scan line in data file */
154   int    ilinelen;       /* length of line within image structure */
155   int    unit;           /* # of bytes in a bitmap unit */
156   int    xoffset;        /* xoffset within line */
157   int    xunits;         /* # of units across the whole scan line */
158   int    trailer;        /* # of bytes in last bitmap unit on a line */
159   int    shift;          /* # of bits to shift last byte set */
160   int    x, y;           /* horizontal and vertical counters */
161   byte  *line;           /* input scan line */
162   byte  *dptr, *iptr;    /* image data pointers */
163   unsigned long (*loader)(); /* unit loading function */
164 
165   image= newBitImage(header.pixmap_width, header.pixmap_height);
166   ilinelen= (header.pixmap_width / 8) + (header.pixmap_width % 8 ? 1 : 0);
167   if (header.bitmap_unit > 7)     /* supposed to be 8, 16, or 32 but appears */
168     unit= header.bitmap_unit / 8; /* to often be the byte count.  this will */
169   else                            /* accept either. */
170     unit= header.bitmap_unit;
171   xoffset= (header.xoffset / (unit * 8)) * unit;
172   if (header.bytes_per_line)
173     dlinelen= header.bytes_per_line;
174   else
175     dlinelen= unit * header.pixmap_width;
176   xunits= (header.pixmap_width / (unit * 8)) +
177     (header.pixmap_width % (unit * 8) ? 1 : 0);
178   trailer= unit - ((xunits * unit) - ilinelen);
179   xunits--; /* we want to use one less than the actual # of units */
180   shift= (unit - trailer) * 8;
181   if (header.byte_order == MSBFirst)
182     loader= doMemToVal;
183   else
184     loader= doMemToValLSB;
185   line= (byte *)lmalloc(dlinelen);
186 
187   for (y= 0; y < header.pixmap_height; y++) {
188     if (zread(zf, (byte *)line, dlinelen) != dlinelen) {
189       fprintf(stderr,
190 	      "%s: Short read while reading data! (returning partial image)\n",
191 	      fullname);
192       lfree(line);
193       return(image);
194     }
195     dptr= line + xoffset;
196     iptr= image->data + (y * ilinelen);
197 
198     if (header.bitmap_bit_order == LSBFirst)
199       flipBits(line, dlinelen);
200 
201     for (x= 0; x < xunits; x++) {
202       valToMem(loader(dptr, unit), iptr, unit);
203       dptr += unit;
204       iptr += unit;
205     }
206 
207     /* take care of last unit on this line
208      */
209 
210     valToMem(loader(dptr, unit) >> shift, iptr, trailer);
211   }
212 
213   lfree(line);
214   return(image);
215 }
216 
217 /* this is a lot like the above function but OR's planes together to
218  * build the destination.  1-bit images are handled by XYBitmap.
219  */
220 
loadXYPixmap(fullname,zf,header)221 static Image *loadXYPixmap(fullname, zf, header)
222      char *fullname;
223      ZFILE *zf;
224      XWDHeader header;
225 { Image *image;
226   int plane;
227   int    dlinelen;       /* length of scan line in data file */
228   int    ilinelen;       /* length of line within image structure */
229   int    unit;           /* # of bytes in a bitmap unit */
230   int    unitbits;       /* # of bits in a bitmap unit */
231   int    unitmask;       /* mask for current bit within current unit */
232   int    xoffset;        /* xoffset within data */
233   int    xunits;         /* # of units across the whole scan line */
234   int    x, x2, y;       /* horizontal and vertical counters */
235   int    index;          /* index within image scan line */
236   byte  *line;           /* input scan line */
237   byte  *dptr, *iptr;    /* image data pointers */
238   unsigned long pixvals; /* bits for pixels in this unit */
239   unsigned long mask;
240   unsigned long (*loader)(); /* unit loading function */
241 
242   image= newRGBImage(header.pixmap_width, header.pixmap_height,
243 		     header.pixmap_depth);
244   ilinelen= image->width * image->pixlen;
245   if (header.bitmap_unit > 7)     /* supposed to be 8, 16, or 32 but appears */
246     unit= header.bitmap_unit / 8; /* to often be the byte count.  this will */
247   else                            /* accept either. */
248     unit= header.bitmap_unit;
249   unitbits= unit * 8;
250   unitmask= 1 << (unitbits - 1);
251   xoffset= (header.xoffset / unitbits) * unit;
252   if (header.bytes_per_line)
253     dlinelen= header.bytes_per_line;
254   else
255     dlinelen= unit * header.pixmap_width;
256   xunits= (header.pixmap_width / (unit * 8)) +
257     (header.pixmap_width % (unit * 8) ? 1 : 0);
258   if (header.byte_order == MSBFirst)
259     loader= doMemToVal;
260   else
261     loader= doMemToValLSB;
262   line= (byte *)lmalloc(dlinelen);
263 
264   /* for each plane, load in the bitmap and or it into the image
265    */
266 
267   for (plane= 0; plane < header.pixmap_depth; plane++) {
268     Pixel plane_mask;
269 
270     plane_mask = (1 << (header.pixmap_depth - plane - 1));
271 
272     for (y= 0; y < header.pixmap_height; y++) {
273       if (zread(zf, (byte *)line, dlinelen) != dlinelen) {
274 	fprintf(stderr,
275 		"%s: Short read while reading data! (returning partial image)\n",
276 		fullname);
277 	lfree(line);
278 	return(image);
279       }
280       dptr= line + xoffset;
281       iptr= image->data + (y * ilinelen);
282       index= 0;
283 
284       if (header.bitmap_bit_order == LSBFirst)
285 	flipBits(line, dlinelen);
286 
287       for (x= 0; x < xunits; x++) {
288 	pixvals= loader(dptr, unit);
289 	mask= unitmask;
290 	for (x2= 0; x2 < unitbits; x2++) {
291 	  if (pixvals & mask)
292 	    valToMem(memToVal(iptr + index, image->pixlen) | plane_mask,
293 		     iptr + index, image->pixlen);
294 	  index += image->pixlen;
295 	  if (index > ilinelen) {
296 	    x= xunits;
297 	    break;
298 	  }
299 	  if (! (mask >>= 1))
300 	    mask= unitmask;
301 	}
302 	dptr += unit;
303       }
304     }
305   }
306 
307   lfree(line);
308   return(image);
309 }
310 
311 /* this loads a ZPixmap format image.  note that this only supports depths
312  * of 4, 8, 16, 24, or 32 bits as does Xlib.  You gotta 6-bit image,
313  * you gotta problem.  1-bit images are handled by XYBitmap.
314  */
315 
loadZPixmap(fullname,zf,header)316 static Image *loadZPixmap(fullname, zf, header)
317      char *fullname;
318      ZFILE *zf;
319      XWDHeader header;
320 { Image *image;
321   int    dlinelen;       /* length of scan line in data file */
322   int    ilinelen;       /* length of scan line in image file */
323   int    depth;          /* depth rounded up to 8-bit value */
324   int    pixlen;         /* length of pixel in bytes */
325   int    x, y;           /* horizontal and vertical counters */
326   byte  *line;           /* input scan line */
327   byte  *dptr, *iptr;    /* image data pointers */
328   unsigned long pixmask; /* bit mask within pixel */
329   unsigned long pixel;   /* pixel we're working on */
330   unsigned long (*loader)(); /* unit loading function */
331 
332   image= newRGBImage(header.pixmap_width, header.pixmap_height,
333 		     header.pixmap_depth);
334 
335   /* for pixmaps that aren't simple depths, we round to a depth of 8.  this
336    * is what Xlib does, be it right nor not.
337    */
338 
339   if ((header.pixmap_depth != 4) && (header.pixmap_depth % 8))
340     depth= header.pixmap_depth + 8 - (header.pixmap_depth % 8);
341   else
342     depth= header.pixmap_depth;
343 
344   pixmask= 0xffffffff >> (32 - header.pixmap_depth);
345   pixlen= image->pixlen;
346   if (header.bytes_per_line)
347     dlinelen= header.bytes_per_line;
348   else
349     dlinelen= depth * header.pixmap_width;
350   ilinelen= image->width * image->pixlen;
351   if (header.byte_order == MSBFirst)
352     loader= doMemToVal;
353   else
354     loader= doMemToValLSB;
355 
356   line= (byte *)lmalloc(dlinelen);
357 
358   for (y= 0; y < header.pixmap_height; y++) {
359     if (zread(zf, (byte *)line, dlinelen) != dlinelen) {
360       fprintf(stderr,
361 	      "%s: Short read while reading data! (returning partial image)\n",
362 	      fullname);
363       lfree(line);
364       return(image);
365     }
366     dptr= line;
367     iptr= image->data + (y * ilinelen);
368 
369     if (header.bitmap_bit_order == LSBFirst)
370       flipBits(line, dlinelen);
371 
372     for (x= 0; x < header.pixmap_width; x++) {
373       switch (depth) {
374       case 4:
375 	pixel= memToVal(dptr, 1);
376 	if (header.bitmap_bit_order == LSBFirst) { /* nybbles are reversed */
377 	  valToMem(pixel & 0xf, iptr++, 1);        /* by flipBits */
378 	  if (++x < header.pixmap_width)
379 	    valToMem(pixel >> 4, iptr++, 1);
380 	}
381 	else {
382 	  valToMem(pixel >> 4, iptr++, 1);
383 	  if (++x < header.pixmap_width)
384 	    valToMem(pixel & 0xf, iptr++, 1);
385 	}
386 	break;
387       case 8:
388 	pixel= ((unsigned long)*(dptr++)) & pixmask; /* loader isn't needed */
389 	valToMem(pixel, iptr++, 1);
390 	break;
391       case 16:
392       case 24:
393       case 32:
394 	valToMem(loader(dptr, pixlen) & pixmask, iptr, pixlen);
395 	dptr += pixlen;
396 	iptr += pixlen;
397 	break;
398       default:
399 	fprintf(stderr,
400 		"%s: ZPixmaps of depth %d are not supported (sorry).\n",
401 		fullname, header.pixmap_depth);
402 	exit(1);
403       }
404     }
405   }
406 
407   lfree(line);
408   return(image);
409 }
410 
xwdLoad(fullname,name,verbose)411 Image *xwdLoad(fullname, name, verbose)
412      char *fullname, *name;
413      int verbose;
414 { ZFILE     *zf;
415   XWDHeader  header;
416   int        cmaplen;
417   XWDColor  *cmap;
418   Image     *image;
419   int        a;
420 
421   image = NULL;
422 
423   if (! (zf= zopen(fullname)))
424     return(NULL);
425   if (! isXWD(name, zf, &header, verbose)) {
426     zclose(zf);
427     return(NULL);
428   }
429 
430   /* complain if we don't understand the visual
431    */
432 
433   switch (header.visual_class) {
434   case StaticGray:
435   case GrayScale:
436   case StaticColor:
437   case PseudoColor:
438     break;
439   case TrueColor:
440   case DirectColor:
441     fprintf(stderr, "Unsupported visual type, sorry\n");
442     exit(1);
443   }
444 
445   if ((header.pixmap_width == 0) || (header.pixmap_height == 0)) {
446     fprintf(stderr, "Zero-size image -- header might be corrupted.\n");
447     exit(1);
448   }
449 
450   /* read in colormap
451    */
452 
453   cmaplen= header.ncolors * sizeof(XWDColor);
454   cmap= (XWDColor *)lmalloc(cmaplen);
455   if (zread(zf, (byte *)cmap, cmaplen) != cmaplen) {
456     fprintf(stderr, "Short read in colormap!\n");
457     exit(1);
458   }
459 
460   /* any depth 1 image is basically a XYBitmap so we fake it here
461    */
462 
463   if (header.pixmap_depth == 1)
464     header.pixmap_format= XYBitmap;
465 
466   /* we can't realistically support images of more than depth 16 with the
467    * RGB image format so this nukes them for the time being.
468    */
469 
470   if (header.pixmap_depth > 16) {
471     fprintf(stderr,
472 	    "%s: Sorry, cannot load images deeper than 16 bits (yet)\n",
473 	    fullname);
474     exit(1);
475   }
476 
477   switch (header.pixmap_format) {
478   case XYBitmap:
479     image= loadXYBitmap(fullname, zf, header);
480     zclose(zf);
481     image->title= dupString(name);
482     return(image); /* we used to goof w/ the cmap but we gave up */
483   case XYPixmap:
484     image= loadXYPixmap(fullname, zf, header);
485     break;
486   case ZPixmap:
487     image= loadZPixmap(fullname, zf, header);
488     break;
489   }
490   zclose(zf);
491   image->title= dupString(name);
492 
493   /* load the colormap.  we should probably use pixval instead of the color
494    * number but the value seems pretty system-dependent and most colormaps
495    * seem to be just dumped in order.
496    */
497 
498   image->rgb.used= header.ncolors;
499   for (a= 0; a < header.ncolors; a++) {
500     image->rgb.red[memToVal(cmap[a].pixel, 4)]= memToVal(cmap[a].red, 2);
501     image->rgb.green[memToVal(cmap[a].pixel, 4)]= memToVal(cmap[a].green, 2);
502     image->rgb.blue[memToVal(cmap[a].pixel, 4)]= memToVal(cmap[a].blue, 2);
503   }
504 
505   lfree((byte *)cmap);
506   return(image);
507 }
508