/* xwd.c: * * XWD file reader. unfortunately the bozo who thought up this format didn't * define anything at all that we can use as an identifier or even to tell * what kind of machine dumped the format. what this does is read the * header and look at several fields to decide if this *might* be an XWD * file and if it is what byte order machine wrote it. * * jim frost 07.24.90 * * Copyright 1990 Jim Frost. See included file "copyright.h" for complete * copyright information. */ #include "copyright.h" #include "xloadimage.h" #include "xwd.h" /* SUPPRESS 558 */ /* this reads the header and does the magic to determine if it is indeed * an XWD file. */ static int isXWD(name, zf, header, verbose) char *name; ZFILE *zf; XWDHeader *header; int verbose; { GenericXWDHeader gh; int a; if (zread(zf, (byte *)&gh, sizeof(GenericXWDHeader)) != sizeof(GenericXWDHeader)) return(0); /* first try -- see if XWD version number matches in either MSB or LSB order */ if (memToVal(gh.file_version, 4) != XWD_VERSION) return(0); /* convert fields to fill out header. things we don't care about * are commented out. */ header->header_size= memToVal(gh.header_size, 4); header->file_version= memToVal(gh.file_version, 4); header->pixmap_format= memToVal(gh.pixmap_format, 4); header->pixmap_depth= memToVal(gh.pixmap_depth, 4); header->pixmap_width= memToVal(gh.pixmap_width, 4); header->pixmap_height= memToVal(gh.pixmap_height, 4); header->xoffset= memToVal(gh.xoffset, 4); header->byte_order= memToVal(gh.byte_order, 4); header->bitmap_unit= memToVal(gh.bitmap_unit, 4); header->bitmap_bit_order= memToVal(gh.bitmap_bit_order, 4); header->bitmap_pad= memToVal(gh.bitmap_pad, 4); header->bits_per_pixel= memToVal(gh.bits_per_pixel, 4); header->bytes_per_line= memToVal(gh.bytes_per_line, 4); header->visual_class= memToVal(gh.visual_class, 4); /*header->red_mask= memToVal(gh.red_mask, 4);*/ /*header->green_mask= memToVal(gh.green_mask, 4);*/ /*header->blue_mask= memToVal(gh.blue_mask, 4);*/ /*header->bits_per_rgb= memToVal(gh.bits_per_rgb, 4);*/ header->colormap_entries= memToVal(gh.colormap_entries, 4); header->ncolors= memToVal(gh.ncolors, 4); /*header->window_width= memToVal(gh.window_width, 4);*/ /*header->window_height= memToVal(gh.window_height, 4);*/ /*header->window_x= memToVal(gh.window_x, 4);*/ /*header->window_y= memToVal(gh.window_y, 4);*/ /*header->window_bdrwidth= memToVal(gh.window_bdrwidth, 4);*/ /* if header size isn't either 100 or 104 bytes, this isn't an XWD file */ if (header->header_size < sizeof(GenericXWDHeader)) return(0); for (a= header->header_size - sizeof(GenericXWDHeader); a; a--) zgetc(zf); /* look at a variety of the XImage fields to see if they are sane. if * they are, this passes our tests. */ switch (header->pixmap_format) { case XYBitmap: case XYPixmap: case ZPixmap: break; default: return(0); } switch (header->visual_class) { case StaticGray: case GrayScale: case StaticColor: case PseudoColor: /* the following are unsupported but recognized */ case TrueColor: case DirectColor: break; default: return(0); } if (verbose) { fprintf(stderr, "%s is a %dx%d XWD image in ", name, header->pixmap_width, header->pixmap_height); switch (header->pixmap_format) { case XYBitmap: fprintf(stderr, "XYBitmap"); break; case XYPixmap: fprintf(stderr, "%d bit XYPixmap", header->pixmap_depth); break; case ZPixmap: fprintf(stderr, "%d bit ZPixmap", header->pixmap_depth); break; } fprintf(stderr, " format\n"); } /* if it got this far, we're pretty damned certain we've got the right * file type and know what order it's in. */ znocache(zf); return(1); } int xwdIdent(fullname, name) char *fullname, *name; { ZFILE *zf; XWDHeader header; int ret; if (! (zf= zopen(fullname))) return(0); ret= isXWD(name, zf, &header, 1); zclose(zf); return(ret); } static Image *loadXYBitmap(fullname, zf, header) char *fullname; ZFILE *zf; XWDHeader header; { Image *image; int dlinelen; /* length of scan line in data file */ int ilinelen; /* length of line within image structure */ int unit; /* # of bytes in a bitmap unit */ int xoffset; /* xoffset within line */ int xunits; /* # of units across the whole scan line */ int trailer; /* # of bytes in last bitmap unit on a line */ int shift; /* # of bits to shift last byte set */ int x, y; /* horizontal and vertical counters */ byte *line; /* input scan line */ byte *dptr, *iptr; /* image data pointers */ unsigned long (*loader)(); /* unit loading function */ image= newBitImage(header.pixmap_width, header.pixmap_height); ilinelen= (header.pixmap_width / 8) + (header.pixmap_width % 8 ? 1 : 0); if (header.bitmap_unit > 7) /* supposed to be 8, 16, or 32 but appears */ unit= header.bitmap_unit / 8; /* to often be the byte count. this will */ else /* accept either. */ unit= header.bitmap_unit; xoffset= (header.xoffset / (unit * 8)) * unit; if (header.bytes_per_line) dlinelen= header.bytes_per_line; else dlinelen= unit * header.pixmap_width; xunits= (header.pixmap_width / (unit * 8)) + (header.pixmap_width % (unit * 8) ? 1 : 0); trailer= unit - ((xunits * unit) - ilinelen); xunits--; /* we want to use one less than the actual # of units */ shift= (unit - trailer) * 8; if (header.byte_order == MSBFirst) loader= doMemToVal; else loader= doMemToValLSB; line= (byte *)lmalloc(dlinelen); for (y= 0; y < header.pixmap_height; y++) { if (zread(zf, (byte *)line, dlinelen) != dlinelen) { fprintf(stderr, "%s: Short read while reading data! (returning partial image)\n", fullname); lfree(line); return(image); } dptr= line + xoffset; iptr= image->data + (y * ilinelen); if (header.bitmap_bit_order == LSBFirst) flipBits(line, dlinelen); for (x= 0; x < xunits; x++) { valToMem(loader(dptr, unit), iptr, unit); dptr += unit; iptr += unit; } /* take care of last unit on this line */ valToMem(loader(dptr, unit) >> shift, iptr, trailer); } lfree(line); return(image); } /* this is a lot like the above function but OR's planes together to * build the destination. 1-bit images are handled by XYBitmap. */ static Image *loadXYPixmap(fullname, zf, header) char *fullname; ZFILE *zf; XWDHeader header; { Image *image; int plane; int dlinelen; /* length of scan line in data file */ int ilinelen; /* length of line within image structure */ int unit; /* # of bytes in a bitmap unit */ int unitbits; /* # of bits in a bitmap unit */ int unitmask; /* mask for current bit within current unit */ int xoffset; /* xoffset within data */ int xunits; /* # of units across the whole scan line */ int x, x2, y; /* horizontal and vertical counters */ int index; /* index within image scan line */ byte *line; /* input scan line */ byte *dptr, *iptr; /* image data pointers */ unsigned long pixvals; /* bits for pixels in this unit */ unsigned long mask; unsigned long (*loader)(); /* unit loading function */ image= newRGBImage(header.pixmap_width, header.pixmap_height, header.pixmap_depth); ilinelen= image->width * image->pixlen; if (header.bitmap_unit > 7) /* supposed to be 8, 16, or 32 but appears */ unit= header.bitmap_unit / 8; /* to often be the byte count. this will */ else /* accept either. */ unit= header.bitmap_unit; unitbits= unit * 8; unitmask= 1 << (unitbits - 1); xoffset= (header.xoffset / unitbits) * unit; if (header.bytes_per_line) dlinelen= header.bytes_per_line; else dlinelen= unit * header.pixmap_width; xunits= (header.pixmap_width / (unit * 8)) + (header.pixmap_width % (unit * 8) ? 1 : 0); if (header.byte_order == MSBFirst) loader= doMemToVal; else loader= doMemToValLSB; line= (byte *)lmalloc(dlinelen); /* for each plane, load in the bitmap and or it into the image */ for (plane= header.pixmap_depth; plane > 0; plane--) { for (y= 0; y < header.pixmap_height; y++) { if (zread(zf, (byte *)line, dlinelen) != dlinelen) { fprintf(stderr, "%s: Short read while reading data! (returning partial image)\n", fullname); lfree(line); return(image); } dptr= line + xoffset; iptr= image->data + (y * ilinelen); index= 0; if (header.bitmap_bit_order == LSBFirst) flipBits(line, dlinelen); for (x= 0; x < xunits; x++) { pixvals= loader(dptr, unit); mask= unitmask; for (x2= 0; x2 < unitbits; x2++) { if (pixvals & mask) valToMem(memToVal(iptr + index, image->pixlen) | (1 << plane), iptr + index, image->pixlen); index += image->pixlen; if (index > ilinelen) { x= xunits; break; } if (! (mask >>= 1)) mask= unitmask; } dptr += unit; } } } lfree(line); return(image); } /* this loads a ZPixmap format image. note that this only supports depths * of 4, 8, 16, 24, or 32 bits as does Xlib. You gotta 6-bit image, * you gotta problem. 1-bit images are handled by XYBitmap. */ static Image *loadZPixmap(fullname, zf, header) char *fullname; ZFILE *zf; XWDHeader header; { Image *image; int dlinelen; /* length of scan line in data file */ int ilinelen; /* length of scan line in image file */ int depth; /* depth rounded up to 8-bit value */ int pixlen; /* length of pixel in bytes */ int x, y; /* horizontal and vertical counters */ byte *line; /* input scan line */ byte *dptr, *iptr; /* image data pointers */ unsigned long pixmask; /* bit mask within pixel */ unsigned long pixel; /* pixel we're working on */ unsigned long (*loader)(); /* unit loading function */ image= newRGBImage(header.pixmap_width, header.pixmap_height, header.pixmap_depth); /* for pixmaps that aren't simple depths, we round to a depth of 8. this * is what Xlib does, be it right nor not. */ if ((header.pixmap_depth != 4) && (header.pixmap_depth % 8)) depth= header.pixmap_depth + 8 - (header.pixmap_depth % 8); else depth= header.pixmap_depth; pixmask= 0xffffffff >> (32 - header.pixmap_depth); pixlen= image->pixlen; if (header.bytes_per_line) dlinelen= header.bytes_per_line; else dlinelen= depth * header.pixmap_width; ilinelen= image->width * image->pixlen; if (header.byte_order == MSBFirst) loader= doMemToVal; else loader= doMemToValLSB; line= (byte *)lmalloc(dlinelen); for (y= 0; y < header.pixmap_height; y++) { if (zread(zf, (byte *)line, dlinelen) != dlinelen) { fprintf(stderr, "%s: Short read while reading data! (returning partial image)\n", fullname); lfree(line); return(image); } dptr= line; iptr= image->data + (y * ilinelen); if (header.bitmap_bit_order == LSBFirst) flipBits(line, dlinelen); for (x= 0; x < header.pixmap_width; x++) { switch (depth) { case 4: pixel= memToVal(dptr, 1); if (header.bitmap_bit_order == LSBFirst) { /* nybbles are reversed */ valToMem(pixel & 0xf, iptr++, 1); /* by flipBits */ if (++x < header.pixmap_width) valToMem(pixel >> 4, iptr++, 1); } else { valToMem(pixel >> 4, iptr++, 1); if (++x < header.pixmap_width) valToMem(pixel & 0xf, iptr++, 1); } break; case 8: pixel= ((unsigned long)*(dptr++)) & pixmask; /* loader isn't needed */ valToMem(pixel, iptr++, 1); break; case 16: case 24: case 32: valToMem(loader(dptr, pixlen) & pixmask, iptr, pixlen); dptr += pixlen; iptr += pixlen; break; default: fprintf(stderr, "%s: ZPixmaps of depth %d are not supported (sorry).\n", fullname, header.pixmap_depth); cleanup(-1); } } } lfree(line); return(image); } Image *xwdLoad(fullname, name, verbose) char *fullname, *name; int verbose; { ZFILE *zf; XWDHeader header; int cmaplen; XWDColor *cmap; Image *image=NULL; int a; if (! (zf= zopen(fullname))) return(NULL); if (! isXWD(name, zf, &header, verbose)) { zclose(zf); return(NULL); } /* complain if we don't understand the visual */ switch (header.visual_class) { case StaticGray: case GrayScale: case StaticColor: case PseudoColor: break; case TrueColor: case DirectColor: fprintf(stderr, "Unsupported visual type, sorry\n"); cleanup(-1); } if ((header.pixmap_width == 0) || (header.pixmap_height == 0)) { fprintf(stderr, "Zero-size image -- header might be corrupted.\n"); cleanup(-1); } /* read in colormap */ cmaplen= header.ncolors * sizeof(XWDColor); cmap= (XWDColor *)lmalloc(cmaplen); if (zread(zf, (byte *)cmap, cmaplen) != cmaplen) { fprintf(stderr, "Short read in colormap!\n"); cleanup(-1); } /* any depth 1 image is basically a XYBitmap so we fake it here */ if (header.pixmap_depth == 1) header.pixmap_format= XYBitmap; /* we can't realistically support images of more than depth 16 with the * RGB image format so this nukes them for the time being. */ if (header.pixmap_depth > 16) { fprintf(stderr, "%s: Sorry, cannot load images deeper than 16 bits (yet)\n", fullname); cleanup(-1); } switch (header.pixmap_format) { case XYBitmap: image= loadXYBitmap(fullname, zf, header); zclose(zf); image->title= dupString(name); return(image); /* we used to goof w/ the cmap but we gave up */ case XYPixmap: image= loadXYPixmap(fullname, zf, header); break; case ZPixmap: image= loadZPixmap(fullname, zf, header); break; } zclose(zf); image->title= dupString(name); /* load the colormap. we should probably use pixval instead of the color * number but the value seems pretty system-dependent and most colormaps * seem to be just dumped in order. */ image->rgb.used= header.ncolors; for (a= 0; a < header.ncolors; a++) { image->rgb.red[memToVal(cmap[a].pixel, 4)]= memToVal(cmap[a].red, 2); image->rgb.green[memToVal(cmap[a].pixel, 4)]= memToVal(cmap[a].green, 2); image->rgb.blue[memToVal(cmap[a].pixel, 4)]= memToVal(cmap[a].blue, 2); } lfree((byte *)cmap); return(image); }