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