/* $XConsortium: pixutils.c,v 5.2 94/04/17 20:46:37 rws Exp $ */ /***************************************************************** Copyright (c) 1989,1990, 1991 X Consortium Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of the X Consortium shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from the X Consortium. Copyright (c) 1989,1990, 1991 by Sun Microsystems, Inc. All Rights Reserved Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the names of Sun Microsystems, and the X Consortium, not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. SUN MICROSYSTEMS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL SUN MICROSYSTEMS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ******************************************************************/ /* * pixel utilities library */ /* * header files */ #include "stdio.h" #include "fcntl.h" #include #include #include "X11/Xlib.h" #include "X11/Xutil.h" #include "logmsg.h" #include "pixutils.h" #include "varargs.h" /* * constants */ #define PSEUDOCOLOR 1 #define MAXIMAGEDIM 400 #define SIZEIMGENTRY 2 #define MAXIMAGEDATASIZE (SIZEIMGENTRY * MAXIMAGEDIM * MAXIMAGEDIM + 4) /* extra 4 bytes to store number of bytes of image data */ #define SIZECMAPENTRY 4 #define MAXCMAPDATASIZE (MAXCOLOR * SIZECMAPENTRY + 2) /* extra 2 bytes to store number of bytes of cmap data */ #define MAXCOLOR 256 #define BUFSIZE 512 #define MAXFILENAMELEN 256 #ifdef SYSV #define R_OK 4 #endif /* * global variables */ static Unsign8 imagedata[MAXIMAGEDATASIZE]; /* image data to be saved */ static Unsign8 cmapdata[MAXCMAPDATASIZE]; /* image data to be saved */ /* * extern funcitons */ char *malloc(); /* * macros */ /* * I_SYNC() synchronize frame buffer access, to be called before pixel read back * It is currently a NOP */ #define I_SYNC() /* * table of contents */ /* exported routines */ void i_check_image(); Unsign8 *i_get_pixel_array(); Colormap i_get_window_cmap(); void i_pixel_to_rgb(); int i_get_bits_per_rgb(); /* local routines */ void i_save_image(); void i_compare_image_to_file(); static void i_get_file_data(); static void i_write(); static void i_read(); static void i_16_to_8(); static void i_8_to_16(); static void i_32_to_8(); static void i_8_to_32(); static void i_store_image(); static void i_store_cmap(); static void i_get_cmap_data(); static Unsign8 i_scale_ushort_to_uchar(); static char *emalloc(); /* * i_check_image(imagename,display,window,x,y,w,h,text) * Top-level image comparison routine. Find a reference image * in one of the standard places; if it exists, compare against * it and take appropriate action. If it doesn't exist, save * a copy in current image directory. */ void i_check_image(imagename, display,window,x,y,w,h,text) char *imagename; /* name of the image file (not full path) */ Display *display; /* X display */ Window window; /* X window */ int x; /* origin x in dc */ int y; /* origin y in dc */ int w; /* width in dc */ int h; /* height in dc */ char *text; /* image description */ { char refpath[MAXFILENAMELEN]; /* full pathname of reference image */ char currpath[MAXFILENAMELEN]; /* full pathname of current image */ char currpathfailed[MAXFILENAMELEN+1]; /* above, marked for failure */ char *currp; /* pointer to previously-saved image*/ /* in currimages directory */ int cmpstatus; unsigned long diffcount; int result; if (inspexdest == NULL) tabort("i_check_image: must define INSPEXDEST, exiting...\n"); if (inspeximageref == NULL) tabort("i_check_image: must define INSPEXIMAGEREF, exiting...\n"); sprintf(currpath, "%s/%s/%s", inspexdest, "currimages", imagename); sprintf(currpathfailed, "%s/%s/%s%s", inspexdest, "currimages", imagename, "-"); /* * find the correct reference image */ /* try INSPEXDEST/localrefimages/imagename */ sprintf(refpath, "%s/%s/%s", inspexdest, "localrefimages", imagename); if (access(refpath,R_OK) == -1) { /* try INSPEXIMAGEREF */ sprintf(refpath, "%s/%s", inspeximageref, imagename); if (access(refpath, R_OK) == -1) { /* no reference image found -- save, print failure message */ tfprintf("failed - no reference image for %s (saving current image)\n", imagename); i_save_image(currpath, display,window,x,y,w,h,text); return; } } /* * Find whether this image has been saved in currimages directory */ if (access(currpath, R_OK) == 0) currp = currpath; else if (access(currpathfailed, R_OK) == 0) currp = currpathfailed; else currp = NULL; /* * Compare sample image with reference image */ i_compare_image_to_file(refpath,display,window,x,y,w,h, &cmpstatus, &diffcount); if (cmpstatus == 0) { /* image matches reference */ if (currp != NULL) { /* a copy of this image exists in currimages - delete it */ if (unlink(currp) == 1) { tvprintf(0,"error: image %s matched, but couldn't delete %s\n", imagename, currp); } } return; } else { /* comparison failed -- see if there's an image in currimage dir. */ if (currp != NULL) { /* there's a readable image in currimages - compare */ i_compare_image_to_file(currp,display,window,x,y,w,h, &cmpstatus, &diffcount); if (cmpstatus == 0) { /* this image matches the one in currimages directory */ if (currp == currpathfailed) { tfprintf("failed - image comparison (%s matched known bad image)\n", imagename); } else { /* image in currimages hasn't been verified yet */ tfprintf("failed, pending manual comparison of image %s\n", imagename); } } else { /* doesn't match image in currimages - overwrite it */ tfprintf("failed, pending manual comparison of image %s\n", imagename); i_save_image(currpath, display,window,x,y,w,h,text); } } else { /* there is no corresponding image in currimages; write one */ tfprintf("failed, pending manual comparison of image %s\n", imagename); i_save_image(currpath, display,window,x,y,w,h,text); } } } /* Image file format: <1 byte : flag image type, currently only support pseudocolor> <2 bytes: number of bytes of ascii text to follow> <2 bytes: width of image> <2 bytes: height of image> <2 bytes: number of colormap data entries> <4 bytes: number of bytes of image data> */ /* * i_save_image(filename,display,window,x,y,w,h,text) save a window image to a fi le */ void i_save_image(filename,display,window,x,y,w,h,text) char *filename; /* reference image file name */ Display *display; /* X display */ Window window; /* X window */ int x; /* origin x in dc */ int y; /* origin y in dc */ int w; /* width in dc */ int h; /* height in dc */ char *text; /* image description */ { int fd; Unsign32 ncoldata,nimgdata; static char pixelflags[MAXCOLOR]; Unsign8 buf[4]; if ((fd = open(filename,O_CREAT|O_WRONLY|O_TRUNC,0666)) == -1) tabort("ERROR: i_save_image: in opening \"%s\", exiting...\n",filename); /* current implementation only supports visual type PSEUDOCOLOR */ if (w > MAXIMAGEDIM) tabort("ERROR: i_save_image: cannot save image of width > limit (%d)\n",MAXIMAGEDIM); if (h > MAXIMAGEDIM) tabort("ERROR: i_save_image: cannot save image of height > limit (%d)\n",MAXIMAGEDIM); buf[0] = PSEUDOCOLOR; i_16_to_8(strlen(text),&buf[1]); /* * store display type <1 byte> and length of text <2 bytes> */ i_write(fd,3,(char*)buf); /* * store text */ i_write(fd,strlen(text),(char*)text); i_16_to_8((unsigned short)w,&buf[0]); i_16_to_8((unsigned short)h,&buf[2]); /* * store image width and height, bits per rgb <5 bytes> */ i_write(fd,4,(char*)buf); i_store_image(display,window,x,y,w,h,pixelflags,&nimgdata,imagedata); i_store_cmap(display,window,pixelflags,&ncoldata,cmapdata); /* * store color map size and data */ i_write(fd,(int)ncoldata,(char*)cmapdata); /* * store image size and data */ i_write(fd,(int)nimgdata,(char*)imagedata); if (close(fd) == -1) tabort("ERROR: i_save_image: in closing \"%s\", exiting...\n",filename); } /* * i_store_image(display,window,x,y,w,h,pixelflags,ndata,imagedata) * Store run length encoded image data into a buffer * * */ static void i_store_image(display,window,x,y,w,h,pixelflags,ndata,imagedata) Display *display; /* X display */ Window window; /* X window */ int x; /* origin x in dc */ int y; /* origin y in dc */ int w; /* width in dc */ int h; /* height in dc */ char *pixelflags; /* OUT pixel flags */ Unsign32 *ndata; /* OUT number of bytes of data */ Unsign8 *imagedata; /* OUT saved image data */ { int i,j; Unsign8 *pixbuf,*pixp,*iptr,lastpix,count; iptr = &imagedata[4]; /* reserved 4 bytes for storing ndata */ *ndata = 0; memset(pixelflags,0,MAXCOLOR); pixp = pixbuf = i_get_pixel_array(display,window,x,y,w,h); for (count=0,i=0;i= 255) { /* count only hold up to 255 */ *iptr++ = count; *iptr++ = lastpix; (*ndata) += 2; count = 0; } } else { /* flush accumulated pixels */ *iptr++ = count; *iptr++ = lastpix; (*ndata) += 2; lastpix = *pixp; count = 1; } } } /* process remaining data */ *iptr++ = count; *iptr++ = lastpix; (*ndata) += SIZEIMGENTRY; free (pixbuf); i_32_to_8(*ndata,&imagedata[0]); (*ndata) += 4; } /* * i_store_cmap(display,window,pixelflags,ndata,cmapdata) * store cmap data into buffer * * */ static void i_store_cmap(display,window,pixelflags,ndata,cmapdata) Display *display; /* X display */ Window window; /* X window */ char *pixelflags; /* pixel flags */ Unsign32 *ndata; /* OUT number of bytes of data */ Unsign8 *cmapdata; /* OUT saved cmap data */ { int i; Unsign16 r,g,b; Colormap cmap; Unsign8 *cp; Unsign16 ncol=0; int bits_per_rgb; bits_per_rgb = i_get_bits_per_rgb(display); cp = &cmapdata[2]; /* reserved two bytes for ncol */ cmap = i_get_window_cmap(display,window); for (i=0;i MAXIMAGEDIM) tabort("ERROR: i_compare_image_to_file: cannot compare image of width > limit (%d)\n",MAXIMAGEDIM); if (h > MAXIMAGEDIM) tabort("ERROR: i_compare_image_to_file: cannot compare image of height > limit (%d)\n",MAXIMAGEDIM); if (w != fw || h != fh ) { tvprintf(1,"ERROR: i_compare_image_to_file: screenimage's dim (%d,%d) != fileimage's (%d,%d)\n", w,h,fw,fh); *status = 1; return; } for (i=0;i BUFSIZE) tabort("i_get_file_data: text too long (%d), exiting...\n",ntext); /* read text */ i_read(fd,(int)ntext,(char*)foo); /* read image's width and height and size of cmap data entries */ i_read(fd,6,(char*)buf); i_8_to_16(&buf[0],w); i_8_to_16(&buf[2],h); i_8_to_16(&buf[4],&ncol); if (ncol > MAXCOLOR) tabort("i_get_file_data: invalid data: number of colors > %d, exiting...\n",MAXCOLOR); /* read cmap data */ i_read(fd,(int)(ncol*SIZECMAPENTRY),(char*)cmapdata); i_get_cmap_data(ncol,cmapdata,cmaptbl); /* read size of image data */ i_read(fd,4,(char*)buf); i_8_to_32(&buf[0],&nbyte); *imagedata = (unsigned char*) emalloc(nbyte); /* read image data */ i_read(fd,(int)nbyte,(char*)*imagedata); } /* * i_get_cmap_data(ncol,cmapdata,cmaptbl); * read cmap data from file and create cmap * * */ static void i_get_cmap_data(ncol,cmapdata,cmaptbl) Unsign16 ncol; /* number of colors */ Unsign8 *cmapdata; /* cmap data */ Unsign8 cmaptbl[][3]; /* OUT color mapping table */ { int i,j; Unsign8 *cp; for (cp=cmapdata,i=0;iclass != PseudoColor) tabort("ERROR: i_get_pixel_array: inpex currently supports only PSEUDOCOLOR, exiting...\n"); /* * Return a pointer to the pixel array. * This routine automatically allocate memory for the pixel buffer */ if (!XGetWindowAttributes(display,window,&attr)) tabort("ERROR: i_get_pixel_array: in XGetWIndowAttributes, exiting... \n"); if (w<=0 || h<=0 || w>MAXIMAGEDIM || h>MAXIMAGEDIM || x < 0 || y <0 || x+w >attr.width || y+h > attr.height) { tabort("i_get_pixel_array: invalid parameters, exiting...\n"); } bp = buf = (Unsign8 *) emalloc(w*h*sizeof(char)); I_SYNC(); XSync(display,0); image = XGetImage(display,window,x,y,w,h,-1,ZPixmap); if (!image) tabort("ERROR: i_get_pixel_array: in XGetImage, exiting...\n"); for (j=0;jbits_per_rgb; #ifdef BITS_PER_RGB_BUG tvprintf(4,"WARNING: test is faking inquiry of bits per rgb of visual\n"); bits_per_rgb = 8; #endif if (bits_per_rgb != 8 && bits_per_rgb != 16) tabort("i_get_bits_per_rgb: unknown bits_per_rgb = %d, exiting...\n", bits_per_rgb); return(bits_per_rgb); } /* * i_pixel_to_rgb(display,cmap,pixel,r,g,b) * retrieve the the RGB components of the pixel */ void i_pixel_to_rgb(display,cmap,pixel,bits_per_rgb,r,g,b) Display *display; /* X display */ Colormap cmap; /* X color map */ unsigned long pixel; /* pixel */ int bits_per_rgb; /* bits per rgb for display */ Unsign8 *r; /* OUT red */ Unsign8 *g; /* OUT green */ Unsign8 *b; /* OUT blue */ { XColor color; color.pixel = pixel; XQueryColor(display,cmap,&color); *r = i_scale_ushort_to_uchar(bits_per_rgb,color.red); *g = i_scale_ushort_to_uchar(bits_per_rgb,color.green); *b = i_scale_ushort_to_uchar(bits_per_rgb,color.blue); } /* * i_scale_ushort_to_uchar(bits_per_rgb,value) */ static Unsign8 i_scale_ushort_to_uchar(bits_per_rgb,value) int bits_per_rgb; unsigned short value; { switch (sizeof(unsigned short)) { case 1: return( (Unsign8 )value ); case 2: switch (bits_per_rgb) { case 8: return( (Unsign8) (value >> 8)); case 16: return( (Unsign8) (value*1.0/65535*255)); default: tabort("i_scale_ushort_to_uchar: unknown bits_per_rgb %d, exiting...\n",bits_per_rgb); } default: tabort("abort: i_scale_ushort_to_uchar: unknown size for unsigned short %d, exiting...\n",sizeof(unsigned short)); } } /* * i_16_to_8(n,buf) convert 1 16-bit to 2 8-bit data */ static void i_16_to_8(n,buf) Unsign16 n; Unsign8 *buf; /* buffer */ { buf[0] = (Unsign8) n; /* extract lower byte */ buf[1] = (Unsign8) (n >> 8); /* extract higher byte */ } /* * i_8_to_16(buf,n) convert 2 8-bit to 1 16-bit data */ static void i_8_to_16(buf,n) Unsign8 *buf; /* buffer */ Unsign16 *n; { /* add up low & high byte */ *n = (Unsign16 ) buf[0] + (((Unsign16 ) buf[1]) << 8); } /* * i_32_to_8(n,buf) convert 1 32-bit to 4 8-bit data */ static void i_32_to_8(n,buf) Unsign32 n; Unsign8 *buf; /* buffer */ { buf[0] = (Unsign8) n; buf[1] = (Unsign8) (n >> 8); buf[2] = (Unsign8) (n >> 16); buf[3] = (Unsign8) (n >> 24); } /* * i_8_to_32(buf,n) convert 4 8-bit to 1 32-bit data */ static void i_8_to_32(buf,n) Unsign8 *buf; /* buffer */ Unsign32 *n; { /* add up all bytes */ *n = (Unsign32) buf[0] + (((Unsign32) buf[1]) << 8) + (((Unsign32) buf[2]) << 16) + (((Unsign32) buf[3]) << 24) ; } /* * i_write(fd,n,buf) write n bytes of data in user buffer */ static void i_write(fd,n,buf) int fd; /* file descriptor */ int n; /* number of bytes */ char *buf; /* data */ { if (write(fd,buf,n) != n) tabort("ERROR: i_write: in write(), exiting...\n"); } /* * i_read(fd,n,buf) read n bytes of data to buffer */ static void i_read(fd,n,buf) int fd; /* file descritor */ int n; /* number of bytes */ char *buf; /* OUT value */ { if (read(fd,buf,n)!= n) tabort("ERROR: i_read: in read(), exiting...\n"); } /* * emalloc(size) allocate memory. */ static char * emalloc(size) int size; { char *p; if (! (p=malloc(size))) tabort("ERROR: emalloc: in malloc(), exiting...\n"); return(p); }