/* #ident "@(#)x11:contrib/clients/xloadimage/send.c 6.21 94/07/29 Labtam" */ /* send.c: * * send an Image to an X pixmap * * jim frost 10.02.89 * * Copyright 1989, 1990, 1991 Jim Frost. * See included file "copyright.h" for complete copyright information. */ #include "copyright.h" #include "xli.h" #include /* extra colors to try allocating in private color maps to minimise flashing */ #define NOFLASH_COLORS 256 /* number of colors to allow in the default colormap */ #define DEFAULT_COLORS 16 static int GotError; static int pixmapErrorTrap(Display *disp, XErrorEvent *pErrorEvent) { #define MAXERRORLEN 100 char buf[MAXERRORLEN+1]; GotError = 1; XGetErrorText(disp, pErrorEvent->error_code, buf, MAXERRORLEN); printf("serial #%ld (request code %d) Got Error %s\n", pErrorEvent->serial, pErrorEvent->request_code, buf); return(0); } Pixmap ximageToPixmap(Display *disp, Window parent, XImageInfo *xii) { int (*old_handler)(); Pixmap pixmap; GotError = 0; old_handler = XSetErrorHandler(pixmapErrorTrap); XSync(disp, False); pixmap= XCreatePixmap(disp, parent, xii->ximage->width, xii->ximage->height, xii->depth); (void)XSetErrorHandler(old_handler); if (GotError) return(None); xii->drawable= pixmap; sendXImage(xii, 0, 0, 0, 0, xii->ximage->width, xii->ximage->height); return(pixmap); } /* find the best pixmap depth supported by the server for a particular * visual and return that depth. * * this is complicated by R3's lack of XListPixmapFormats so we fake it * by looking at the structure ourselves. */ static unsigned int bitsPerPixelAtDepth(Display *disp, int scrn, unsigned int depth) { #if defined(XlibSpecificationRelease) && (XlibSpecificationRelease >= 4) /* the way things are */ XPixmapFormatValues *xf; int nxf, a; xf = XListPixmapFormats(disp, &nxf); for (a = 0; a < nxf; a++) if (xf[a].depth == depth) { int bpp; bpp = xf[a].bits_per_pixel; XFree(xf); return (unsigned int) bpp; } XFree(xf); #else /* the way things were (X11R3) */ unsigned int a; for (a= 0; a < disp->nformats; a++) if (disp->pixmap_format[a].depth == depth) return(disp->pixmap_format[a].bits_per_pixel); #endif /* this should never happen; if it does, we're in trouble */ fprintf(stderr, "bitsPerPixelAtDepth: Can't find pixmap depth info!\n"); exit(1); } static int shmErrorTrap(Display *d, XErrorEvent *ev) { GotError = 1; if (ev->error_code != BadAccess) pixmapErrorTrap(d, ev); return 0; } static void createImage(XImageInfo *xii, Image *image, Visual *visual, int depth, int format) { if (XShmQueryExtension(xii->disp)) { xii->ximage = XShmCreateImage(xii->disp, visual, depth, format, NULL, &xii->shm, image->width, image->height); xii->shm.shmid = shmget(IPC_PRIVATE, xii->ximage->bytes_per_line * image->height, IPC_CREAT | 0777); if (xii->shm.shmid >= 0) { int ret; XErrorHandler old_handler; xii->ximage->data = xii->shm.shmaddr = shmat(xii->shm.shmid, 0, 0); xii->shm.readOnly = False; XSync(xii->disp, False); GotError = 0; old_handler = XSetErrorHandler(shmErrorTrap); ret = XShmAttach(xii->disp, &xii->shm); XSync(xii->disp, False); (void) XSetErrorHandler(old_handler); if (ret && !GotError) { shmctl(xii->shm.shmid, IPC_RMID, 0); return; } shmdt(xii->shm.shmaddr); shmctl(xii->shm.shmid, IPC_RMID, 0); xii->shm.shmid = -1; } XDestroyImage(xii->ximage); } xii->ximage = XCreateImage(xii->disp, visual, depth, format, 0, NULL, image->width, image->height, 8, 0); xii->ximage->data = lmalloc(image->height * xii->ximage->bytes_per_line); } XImageInfo *imageToXImage(Display *disp, int scrn, Visual *visual, unsigned int ddepth, Image *image, unsigned int private_cmap, unsigned int fit, ImageOptions *options) { float display_gamma = globals.display_gamma; unsigned int verbose = globals.verbose; Pixel *redvalue, *greenvalue, *bluevalue; unsigned int a, b, c=0, newmap, x, y, dpixlen, dbits; XColor xcolor; XImageInfo *xii; Image *orig_image; boolean dogamma=FALSE; int gammamap[256]; CURRFUNC("imageToXImage"); /* set up to adjust the image gamma for the display gamma we've got */ if(GAMMA_NOT_EQUAL(display_gamma,image->gamma) && !(BITMAPP(image))) { make_gamma(display_gamma/image->gamma,gammamap); dogamma = TRUE; } xcolor.flags= DoRed | DoGreen | DoBlue; redvalue= greenvalue= bluevalue= NULL; orig_image= image; xii= (XImageInfo *)lmalloc(sizeof(XImageInfo)); xii->disp= disp; xii->scrn= scrn; xii->depth= 0; xii->drawable= None; xii->index= NULL; xii->rootimage= FALSE; /* assume not */ xii->foreground= xii->background= 0; xii->gc= NULL; xii->ximage= NULL; xii->shm.shmid = -1; /* process image based on type of visual we're sending to */ switch (image->type) { case ITRUE: switch (visual->class) { case TrueColor: case DirectColor: /* goody goody */ break; default: if (visual->map_entries > 2) { Image *dimage; int ncols = visual->map_entries; if(ncols >= 256) ncols -= DEFAULT_COLORS; /* allow for some default colors */ dimage = reduce(image, ncols, options->colordither, display_gamma, verbose); if(dimage != image && orig_image != image) freeImage(image); image = dimage; if(GAMMA_NOT_EQUAL(display_gamma,image->gamma)) { make_gamma(display_gamma/image->gamma,gammamap); dogamma = TRUE; } } else { /* it must be monochrome */ Image *dimage; dimage= dither(image, verbose); if(dimage != image && orig_image != image) freeImage(image); image = dimage; } } break; case IRGB: switch(visual->class) { case TrueColor: case DirectColor: /* no problem, we handle this just fine */ break; default: if (visual->map_entries <= 2) { /* monochrome */ Image *dimage; dimage= dither(image, verbose); if(dimage != image && orig_image != image) freeImage(image); image = dimage; } break; } case IBITMAP: /* no processing ever needs to be done for bitmaps */ break; } /* do color allocation */ switch (visual->class) { case TrueColor: case DirectColor: { Pixel pixval; unsigned int redcolors, greencolors, bluecolors; unsigned int redstep, greenstep, bluestep; unsigned int redbottom, greenbottom, bluebottom; unsigned int redtop, greentop, bluetop; redvalue= (Pixel *)lmalloc(sizeof(Pixel) * 256); greenvalue= (Pixel *)lmalloc(sizeof(Pixel) * 256); bluevalue= (Pixel *)lmalloc(sizeof(Pixel) * 256); if (visual == DefaultVisual(disp, scrn)) xii->cmap= DefaultColormap(disp, scrn); else xii->cmap= XCreateColormap(disp, RootWindow(disp, scrn), visual, AllocNone); retry_direct: /* tag we hit if a DirectColor allocation fails on * default colormap */ /* calculate number of distinct colors in each band */ redcolors= greencolors= bluecolors= 1; for (pixval= 1; pixval; pixval <<= 1) { if (pixval & visual->red_mask) redcolors <<= 1; if (pixval & visual->green_mask) greencolors <<= 1; if (pixval & visual->blue_mask) bluecolors <<= 1; } /* sanity check */ if ((redcolors > visual->map_entries) || (greencolors > visual->map_entries) || (bluecolors > visual->map_entries)) { fprintf(stderr, "Warning: inconsistency in color information (this may be ugly)\n"); } redstep = 256 / redcolors; greenstep = 256 / greencolors; bluestep = 256 / bluecolors; redbottom = greenbottom = bluebottom = 0; redtop = greentop = bluetop = 0; for (a = 0; a < visual->map_entries; a++) { if (redbottom < 256) redtop= redbottom + redstep; if (greenbottom < 256) greentop= greenbottom + greenstep; if (bluebottom < 256) bluetop= bluebottom + bluestep; xcolor.red= (redtop - 1) << 8; xcolor.green= (greentop - 1) << 8; xcolor.blue= (bluetop - 1) << 8; if (! XAllocColor(disp, xii->cmap, &xcolor)) { /* if an allocation fails for a DirectColor default visual then * we should create a private colormap and try again. */ if ((visual->class == DirectColor) && (visual == DefaultVisual(disp, scrn))) { xii->cmap= XCreateColormap(disp, RootWindow(disp, scrn), visual, AllocNone); goto retry_direct; } /* something completely unexpected happened */ fprintf(stderr, "imageToXImage: XAllocColor failed on a TrueColor/Directcolor visual\n"); lfree((byte *) redvalue); lfree((byte *) greenvalue); lfree((byte *) bluevalue); lfree((byte *) xii); return(NULL); } /* fill in pixel values for each band at this intensity */ while ((redbottom < 256) && (redbottom < redtop)) redvalue[redbottom++]= xcolor.pixel & visual->red_mask; while ((greenbottom < 256) && (greenbottom < greentop)) greenvalue[greenbottom++]= xcolor.pixel & visual->green_mask; while ((bluebottom < 256) && (bluebottom < bluetop)) bluevalue[bluebottom++]= xcolor.pixel & visual->blue_mask; } } break; default: /* Not TrueColor or DirectColor */ retry: /* this tag is used when retrying because we couldn't get a fit */ xii->index= (Pixel *)lmalloc(sizeof(Pixel) * (image->rgb.used+NOFLASH_COLORS)); /* private_cmap flag is invalid if not a dynamic visual */ switch (visual->class) { case StaticColor: case StaticGray: private_cmap= 0; } /* get the colormap to use. */ if (private_cmap) { /* user asked us to use a private cmap */ newmap= 1; fit= 0; } else if ((visual == DefaultVisual(disp, scrn)) || (visual->class == StaticGray) || (visual->class == StaticColor)) { /* if we're using the default visual, try to alloc colors shareable. * otherwise we're using a static visual and should treat it * accordingly. */ if (visual == DefaultVisual(disp, scrn)) xii->cmap= DefaultColormap(disp, scrn); else xii->cmap= XCreateColormap(disp, RootWindow(disp, scrn), visual, AllocNone); newmap= 0; /* allocate colors shareable (if we can) */ for (a= 0; a < image->rgb.used; a++) { if (dogamma) { xcolor.red= GAMMA16(*(image->rgb.red + a)); xcolor.green= GAMMA16(*(image->rgb.green + a)); xcolor.blue= GAMMA16(*(image->rgb.blue + a)); } else { xcolor.red= *(image->rgb.red + a); xcolor.green= *(image->rgb.green + a); xcolor.blue= *(image->rgb.blue + a); } if (! XAllocColor(disp, xii->cmap, &xcolor)) { if ((visual->class == StaticColor) || (visual->class == StaticGray)) { printf("imageToXImage: XAllocColor failed on a static visual\n"); xii->no = a; freeXImage(image, xii); return(NULL); } else { /* we can't allocate the colors shareable so free all the colors * we had allocated and create a private colormap (or fit * into the default cmap if `fit' is true). */ XFreeColors(disp, xii->cmap, xii->index, a, 0); xii->no = 0; newmap= 1; break; } } *(xii->index + a)= xcolor.pixel; } xii->no = a; /* number of pixels allocated in default visual */ } else { newmap= 1; fit= 0; } if (newmap) { if (fit) { /* fit the image into the map we have. Fitting the * colors is hard, we have to: * 1. count the available colors using XAllocColorCells. * 2. reduce the depth of the image to fit. * 3. grab the server so no one can goof with the colormap. * 4. free the colors we just allocated. * 5. allocate the colors again shareable. * 6. ungrab the server and continue on our way. */ if (verbose) printf(" Fitting image into default colormap\n"); for (a= 0; a < image->rgb.used; a++) /* count entries we got */ if (! XAllocColorCells(disp, xii->cmap, False, NULL, 0, xii->index + a, 1)) break; if (a <= 2) { Image *dimage; if (verbose) { printf(" Cannot fit into default colormap, dithering..."); fflush(stdout); } dimage= dither(image, 0); if(dimage != image && orig_image != image) freeImage(image); image = dimage; if (verbose) printf("done\n"); fit= 0; if(a > 0) XFreeColors(disp, xii->cmap, xii->index, a, 0); lfree((byte *) xii->index); xii->index = NULL; goto retry; } if (a < image->rgb.used) { /* if image has too many colors, reduce it */ Image *dimage; dimage= reduce(image, a, options->colordither, display_gamma, verbose); if(dimage != image && orig_image != image) freeImage(image); image = dimage; if(GAMMA_NOT_EQUAL(display_gamma,image->gamma)) { /* reduce may have changed it */ make_gamma(display_gamma/image->gamma,gammamap); dogamma = TRUE; } } XGrabServer(disp); /* stop someone else snarfing the colors */ XFreeColors(disp, xii->cmap, xii->index, a, 0); for (a= 0; a < image->rgb.used; a++) { if(dogamma) { xcolor.red= GAMMA16(*(image->rgb.red + a)); xcolor.green= GAMMA16(*(image->rgb.green + a)); xcolor.blue= GAMMA16(*(image->rgb.blue + a)); } else { xcolor.red= *(image->rgb.red + a); xcolor.green= *(image->rgb.green + a); xcolor.blue= *(image->rgb.blue + a); } /* if this fails we're in trouble */ if (! XAllocColor(disp, xii->cmap, &xcolor)) { printf("XAllocColor failed while fitting colormap!\n"); XUngrabServer(disp); xii->no = a; freeXImage(image, xii); return(NULL); } *(xii->index + a)= xcolor.pixel; } XUngrabServer(disp); xii->no = a; } else { /* not fit */ /* * we create a private cmap and allocate * the colors writable. */ if (verbose) printf(" Using private colormap\n"); /* create new colormap */ xii->cmap= XCreateColormap(disp, RootWindow(disp, scrn), visual, AllocNone); for (a= 0; a < image->rgb.used; a++) /* count entries we got */ if (! XAllocColorCells(disp, xii->cmap, False, NULL, 0, xii->index + a, 1)) break; if (a == 0) { fprintf(stderr, "imageToXImage: Color allocation failed!\n"); lfree((byte *) xii->index); xii->index = NULL; freeXImage(image, xii); return(NULL); } if (a < image->rgb.used) { /* if image has too many colors, reduce it */ Image *dimage; dimage= reduce(image, a, options->colordither, display_gamma, verbose); if(dimage != image && orig_image != image) freeImage(image); image = dimage; if(GAMMA_NOT_EQUAL(display_gamma,image->gamma)) { /* reduce may have changed it */ make_gamma(display_gamma/image->gamma,gammamap); dogamma = TRUE; } } /* See if we can allocate some more colors for a few */ /* entries from the default color map to reduce flashing.*/ for(c=0; c < NOFLASH_COLORS; c++) { if (! XAllocColorCells(disp, xii->cmap, False, NULL, 0, xii->index + a + c, 1)) break; } for(b=0; b < c; b++) { /* set default colors */ xcolor.pixel = *(xii->index + b); XQueryColor(disp, DefaultColormap(disp, scrn), &xcolor); XStoreColor(disp, xii->cmap, &xcolor); } if(dogamma) { for (b=0; b < a; b++) { xcolor.pixel= *(xii->index + c + b); xcolor.red= GAMMA16(*(image->rgb.red + b)); xcolor.green= GAMMA16(*(image->rgb.green + b)); xcolor.blue= GAMMA16(*(image->rgb.blue + b)); XStoreColor(disp, xii->cmap, &xcolor); } } else { for (b=0; b < a; b++) { xcolor.pixel= *(xii->index + c + b); xcolor.red= *(image->rgb.red + b); xcolor.green= *(image->rgb.green + b); xcolor.blue= *(image->rgb.blue + b); XStoreColor(disp, xii->cmap, &xcolor); } } xii->no = a + c; } } break; } /* create an XImage and related colormap based on the image type * we have. */ if (verbose) { printf(" Building XImage..."); fflush(stdout); } switch (image->type) { case IBITMAP: { byte *dst, *src; int rowbytes; createImage(xii, image, visual, 1, XYBitmap); dst = xii->ximage->data; src = image->data; rowbytes = (image->width + 7) / 8; assert(rowbytes <= xii->ximage->bytes_per_line); for (y = image->height; y--;) { memcpy(dst, src, rowbytes); if (LSBFirst == xii->ximage->bitmap_bit_order) flipBits(dst, rowbytes); dst += xii->ximage->bytes_per_line; src += rowbytes; } xii->depth= ddepth; if(visual->class == DirectColor || visual->class == TrueColor) { Pixel pixval; dbits= bitsPerPixelAtDepth(disp, scrn, ddepth); dpixlen= (dbits + 7) / 8; pixval= redvalue[image->rgb.red[0] >> 8] | greenvalue[image->rgb.green[0] >> 8] | bluevalue[image->rgb.blue[0] >> 8]; xii->background = pixval; pixval= redvalue[image->rgb.red[1] >> 8] | greenvalue[image->rgb.green[1] >> 8] | bluevalue[image->rgb.blue[1] >> 8]; xii->foreground = pixval; } else { /* Not Direct or True Color */ xii->foreground= *((xii->index + c) + 1); xii->background= *(xii->index + c); } break; } case IRGB: case ITRUE: /* modify image data to match visual and colormap */ dbits= bitsPerPixelAtDepth(disp, scrn, ddepth); /* bits per pixel */ xii->depth= ddepth; dpixlen= (dbits + 7) / 8; /* bytes per pixel */ switch (visual->class) { case DirectColor: case TrueColor: { byte *data, *dst, *src; Pixel *pixels, *p; createImage(xii, image, visual, ddepth, ZPixmap); pixels = (Pixel *) lmalloc(image->width * sizeof(Pixel)); src = image->data; data = xii->ximage->data; for (y = image->height; y--;) { for (p = pixels, x = image->width; x--; src += image->pixlen, ++p) *p = memToVal(src, image->pixlen); if (IRGB == image->type) { for (p = pixels, x = image->width; x--; ++p) { *p = RGB_TO_TRUE(image->rgb.red[*p], image->rgb.green[*p], image->rgb.blue[*p]); } } if (dogamma) { for (p = pixels, x = image->width; x--; ++p) { *p = RGB_TO_TRUE(GAMMA8(TRUE_RED(*p)) << 8, GAMMA8(TRUE_GREEN(*p)) << 8, GAMMA8(TRUE_BLUE(*p)) << 8); } } for (p = pixels, x = image->width; x--; ++p) { *p = redvalue[TRUE_RED(*p)] | greenvalue[TRUE_GREEN(*p)] | bluevalue[TRUE_BLUE(*p)]; } dst = data; if (MSBFirst == xii->ximage->byte_order) { for (p = pixels, x = image->width; x--; ++p, dst += dpixlen) valToMem(*p, dst, dpixlen); } else { for (p = pixels, x = image->width; x--; ++p, dst += dpixlen) valToMemLSB(*p, dst, dpixlen); } data += xii->ximage->bytes_per_line; } lfree((byte *) pixels); break; } default: /* only IRGB images make it this far. */ createImage(xii, image, visual, ddepth, ZPixmap); /* if our XImage doesn't have modulus 8 bits per pixel, it's unclear * how to pack bits so we use XPutPixel. this is slower. */ if (dbits % 8) { byte *src; src = image->data; for (y = 0; y < image->height; ++x) { for (x = 0; x < image->width; ++x) { XPutPixel(xii->ximage, x, y, xii->index[c + memToVal(src, image->pixlen)]); src += image->pixlen; } } } else { byte *data, *src, *dst; data = xii->ximage->data; src = image->data; for (y = image->height; y--;) { dst = data; if (MSBFirst == xii->ximage->byte_order) { for (x = image->width; x--; src += image->pixlen, dst += dpixlen) { valToMem(xii->index[c + memToVal(src, image->pixlen)], dst, dpixlen); } } else { for (x = image->width; x--; src += image->pixlen, dst += dpixlen) { valToMemLSB(xii->index[c + memToVal(src, image->pixlen)], dst, dpixlen); } } data += xii->ximage->bytes_per_line; } } break; } } if (verbose) printf("done\n"); if (redvalue) { lfree((byte *)redvalue); lfree((byte *)greenvalue); lfree((byte *)bluevalue); } if (verbose && dogamma) printf(" Have adjusted image from %4.2f to display gamma of %4.2f\n",image->gamma,display_gamma); if (image != orig_image) freeImage(image); return(xii); } /* Given an XImage and a drawable, move a rectangle from the Ximage * to the drawable. */ void sendXImage(XImageInfo *xii, int src_x, int src_y, int dst_x, int dst_y, unsigned w, unsigned h) { XGCValues gcv; /* build and cache the GC */ if (!xii->gc) { gcv.function= GXcopy; if (xii->ximage->depth == 1) { gcv.foreground= xii->foreground; gcv.background= xii->background; xii->gc= XCreateGC(xii->disp, xii->drawable, GCFunction | GCForeground | GCBackground, &gcv); } else xii->gc= XCreateGC(xii->disp, xii->drawable, GCFunction, &gcv); } if (src_x < 0 || src_y < 0 || src_x + w > xii->ximage->width || src_y + h > xii->ximage->height) return; if (xii->shm.shmid >= 0) { XShmPutImage(xii->disp, xii->drawable, xii->gc, xii->ximage, src_x, src_y, dst_x, dst_y, w, h, False); XSync(xii->disp, False); } else { XPutImage(xii->disp, xii->drawable, xii->gc, xii->ximage, src_x, src_y, dst_x, dst_y, w, h); } } /* free up anything cached in the local Ximage structure. */ void freeXImage(Image *image, XImageInfo *xii) { if (xii->index != NULL) { /* if we allocated colors */ if (xii->no > 0 && !xii->rootimage) /* don't free root colors */ XFreeColors(xii->disp, xii->cmap, xii->index, xii->no, 0); lfree((byte *) xii->index); } if (xii->gc) XFreeGC(xii->disp, xii->gc); if (xii->shm.shmid >= 0) { XShmDetach(xii->disp, &xii->shm); shmdt(xii->shm.shmaddr); shmctl(xii->shm.shmid, IPC_RMID, 0); } else { lfree((byte *) xii->ximage->data); } xii->ximage->data= NULL; XDestroyImage(xii->ximage); lfree((byte *) xii); /* should we free private color map to ??? */ }