/* Send an Image to an X pixmap By Jim Frost 1989.10.02, Bryan Henderson 2006.03.25. Copyright 1989, 1990, 1991 Jim Frost. See COPYRIGHT file for copyright information. */ #include #include #include #include "pm_c_util.h" #include "pm.h" #include "mallocvar.h" #include "ximageinfo.h" #include "valtomem.h" #include "image.h" #include "send.h" #define TRUE_TO_15BIT(PIXEL) \ ((unsigned short) \ ((((PIXEL) & 0xf80000) >> 9) | \ (((PIXEL) & 0x00f800) >> 6) | \ (((PIXEL) & 0x0000f8) >> 3))) #define RED_INTENSITY(P) (((P) & 0x7c00) >> 10) #define GREEN_INTENSITY(P) (((P) & 0x03e0) >> 5) #define BLUE_INTENSITY(P) ((P) & 0x001f) #define PM_SCALE(a, b, c) (long)((a) * (c))/(b) static bool GotError; static int pixmapErrorTrap(Display * const disp, XErrorEvent * const pErrorEvent) { #define MAXERRORLEN 100 char buf[MAXERRORLEN+1]; GotError = 1; XGetErrorText(disp, pErrorEvent->error_code, buf, MAXERRORLEN); pm_message("serial #%ld (request code %d) Got Error '%s'", pErrorEvent->serial, pErrorEvent->request_code, buf); return 0; } Pixmap ximageToPixmap(Display * const disp, Window const parent, XImageInfo * const ximageinfoP) { XErrorHandler old_handler; Pixmap pixmap; GotError = FALSE; old_handler = XSetErrorHandler(pixmapErrorTrap); XSync(disp, False); pixmap= XCreatePixmap(disp, parent, ximageinfoP->ximageP->width, ximageinfoP->ximageP->height, ximageinfoP->depth); XSetErrorHandler(old_handler); if (GotError) return None; ximageinfoP->drawable = pixmap; sendXImage(ximageinfoP, 0, 0, 0, 0, ximageinfoP->ximageP->width, ximageinfoP->ximageP->height); return pixmap; } /* find the best pixmap depth the server can do 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 * const disp, int const scrn, unsigned int const depth) { #if XlibSpecificationRelease < 4 /* the way things were */ 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); #else /* the way things should be */ XPixmapFormatValues *xf; unsigned int nxf, a; xf = XListPixmapFormats(disp, (int *)&nxf); for (a = 0; a < nxf; a++) if (xf[a].depth == depth) return(xf[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 void findColors(const Image * const imageP, unsigned int * const pixelCt) { /*---------------------------------------------------------------------------- Find the colors in the Itrue raster 'raster'; return the histogram of those colors as 'pixelCt'. -----------------------------------------------------------------------------*/ unsigned short color; unsigned char * pixel; unsigned int y; for (color = 0; color < 32768; ++color) pixelCt[color] = 0; /* initial value */ for (y = 0, pixel = imageP->data; y < imageP->height; ++y) { unsigned int x; for (x = 0; x < imageP->width; ++x) { unsigned int const color = TRUE_TO_15BIT(memToVal(pixel, 3)); ++pixelCt[color]; pixel += 3; } } } static Image * pseudoColorImageFromItrue(Image * const imageP, unsigned int const ddepth) { /*---------------------------------------------------------------------------- A pseudoColor image from the RGB image *imageP. We compute the index -- i.e. assign each color in *imageP a number. -----------------------------------------------------------------------------*/ unsigned int const maxColorCt = (1 << ddepth); unsigned int y; unsigned int colorCt; /* Number of colors we've indexed so far */ unsigned int pixelCt[32768]; /* colorCt[x] is the number of pixels of color 'x' in the image, where 'x' is the 15 bit RGB representation of the color. */ unsigned int colorIndex[32768]; /* colorIndex[x] is the color index we assigned to the color 'x', where 'x' is the 15 bit RGB representation of the color. */ Pixel pixval; unsigned char * dpixel; Image * newImageP; unsigned short color; /* A color, in 15 bit RGB */ unsigned char * pixel; newImageP = newRGBImage(imageP->width, imageP->height, ddepth); findColors(imageP, pixelCt); for (color = 0, colorCt = 0; color < 32768 && colorCt <= maxColorCt; ++color) { if (pixelCt[color] > 0) { unsigned long const red = RED_INTENSITY(color); unsigned long const grn = GREEN_INTENSITY(color); unsigned long const blu = BLUE_INTENSITY(color); /* Put the color in the color map */ newImageP->rgb.red[colorCt] = red<<11; newImageP->rgb.grn[colorCt] = grn<<11; newImageP->rgb.blu[colorCt] = blu<<11; /* Reverse-index it */ colorIndex[color] = colorCt; ++colorCt; } } newImageP->rgb.used = colorCt; for (y = 0, pixel = imageP->data, dpixel = newImageP->data; y < imageP->height; ++y) { unsigned int x; for (x = 0; x < imageP->width; ++x) { unsigned short const color = TRUE_TO_15BIT(memToVal(pixel, 3)); pixval = colorIndex[color]; valToMem(pixval, dpixel, newImageP->pixlen); pixel += 3; dpixel += newImageP->pixlen; } } newImageP->rgb.compressed = 1; return newImageP; } static void makeUsableVisual(Image * const origImageP, Visual * const visualP, unsigned int const ddepth, Image ** const newImagePP) { /* process image based on type of visual to which we're sending */ switch (origImageP->type) { case ITRUE: switch (visualP->class) { case TrueColor: case DirectColor: /* goody goody */ *newImagePP = origImageP; break; case PseudoColor: *newImagePP = pseudoColorImageFromItrue(origImageP, ddepth); if (*newImagePP == NULL) pm_error("Unable to convert for Pseudocolor."); break; default: pm_error("INTERNAL ERROR: impossible visual class %u", visualP->class); } break; case IRGB: switch(visualP->class) { case TrueColor: case DirectColor: /* no problem, we handle this just fine */ *newImagePP = origImageP; break; default: pm_error("INTERNAL ERROR: impossible visual class %u", visualP->class); } case IBITMAP: /* no processing ever needs to be done for bitmaps */ *newImagePP = origImageP; break; } } static void makeColorMap1(Display * const disp, int const scrn, Visual * const visualP, Colormap * const cmapP, Pixel ** const redvalueP, Pixel ** const grnvalueP, Pixel ** const bluvalueP) { Pixel * redvalue; Pixel * grnvalue; Pixel * bluvalue; Pixel pixval; unsigned int redcolors, grncolors, blucolors; unsigned int redstep, grnstep, blustep; unsigned int redbottom, grnbottom, blubottom; unsigned int redtop, grntop, blutop; unsigned int a; MALLOCARRAY_NOFAIL(redvalue, 256); MALLOCARRAY_NOFAIL(grnvalue, 256); MALLOCARRAY_NOFAIL(bluvalue, 256); if (visualP == DefaultVisual(disp, scrn)) *cmapP = DefaultColormap(disp, scrn); else *cmapP = XCreateColormap(disp, RootWindow(disp, scrn), visualP, AllocNone); retry_direct: /* tag we hit if a DirectColor allocation fails on * default colormap */ /* calculate number of distinct colors in each band */ redcolors = grncolors = blucolors = 1; for (pixval = 1; pixval; pixval <<= 1) { if (pixval & visualP->red_mask) redcolors <<= 1; if (pixval & visualP->green_mask) grncolors <<= 1; if (pixval & visualP->blue_mask) blucolors <<= 1; } /* sanity check */ if ((redcolors > visualP->map_entries) || (grncolors > visualP->map_entries) || (blucolors > visualP->map_entries)) { pm_message("Warning: inconsistency in color information " "(this may be ugly)"); } redstep= 256 / redcolors; grnstep= 256 / grncolors; blustep= 256 / blucolors; redbottom = grnbottom = blubottom= 0; for (a = 0; a < visualP->map_entries; ++a) { XColor xcolor; Status rc; if (redbottom < 256) redtop = redbottom + redstep; if (grnbottom < 256) grntop = grnbottom + grnstep; if (blubottom < 256) blutop = blubottom + blustep; xcolor.flags = DoRed | DoGreen | DoBlue; xcolor.red = (redtop - 1) << 8; xcolor.green = (grntop - 1) << 8; xcolor.blue = (blutop - 1) << 8; rc = XAllocColor(disp, *cmapP, &xcolor); if (rc == 0) { /* Allocation failed. If it's for a DirectColor default visual then we should create a private colormap and try again. */ if ((visualP->class == DirectColor) && (visualP == DefaultVisual(disp, scrn))) { *cmapP = XCreateColormap(disp, RootWindow(disp, scrn), visualP, AllocNone); goto retry_direct; } /* something completely unexpected happened */ pm_error("INTERNAL ERROR: XAllocColor failed on a " "TrueColor/Directcolor visual"); } /* fill in pixel values for each band at this intensity */ while ((redbottom < 256) && (redbottom < redtop)) redvalue[redbottom++] = xcolor.pixel & visualP->red_mask; while ((grnbottom < 256) && (grnbottom < grntop)) grnvalue[grnbottom++] = xcolor.pixel & visualP->green_mask; while ((blubottom < 256) && (blubottom < blutop)) bluvalue[blubottom++] = xcolor.pixel & visualP->blue_mask; } *redvalueP = redvalue; *grnvalueP = grnvalue; *bluvalueP = bluvalue; } static void allocColorCells(Display * const disp, Colormap const cmap, Pixel * const colorIndex, unsigned int const colorCount, unsigned int * const cellCountP) { bool outOfCells; unsigned int cellCount; outOfCells = false; /* initial value */ cellCount = 0; /* initial value */ while (cellCount < colorCount && !outOfCells) { Status rc; rc = XAllocColorCells(disp, cmap, FALSE, NULL, 0, &colorIndex[cellCount++], 1); if (rc == 0) outOfCells = true; } *cellCountP = cellCount; } static void makeColorMap2(Display * const disp, int const scrn, Visual * const visualP, RGBMap const rgb, bool const userWantsPrivateCmap, bool const userWantsFit, bool const verbose, Colormap * const cmapP, Pixel ** const colorIndexP) { bool privateCmap; bool fit; bool newmap; Pixel * colorIndex; MALLOCARRAY_NOFAIL(colorIndex, rgb.used); /* 'privateCmap' is invalid if not a dynamic visual */ switch (visualP->class) { case StaticColor: case StaticGray: privateCmap = TRUE; default: privateCmap = userWantsPrivateCmap; } /* get the colormap to use. */ if (privateCmap) { /* user asked us to use a private cmap */ newmap = TRUE; fit = FALSE; } else if ((visualP == DefaultVisual(disp, scrn)) || (visualP->class == StaticGray) || (visualP->class == StaticColor) || (visualP->class == TrueColor) || (visualP->class == DirectColor)) { unsigned int a; fit = userWantsFit; /* 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 (visualP == DefaultVisual(disp, scrn)) *cmapP = DefaultColormap(disp, scrn); else *cmapP = XCreateColormap(disp, RootWindow(disp, scrn), visualP, AllocNone); newmap = FALSE; /* allocate colors shareable (if we can) */ for (a = 0; a < rgb.used; ++a) { Status rc; XColor xcolor; xcolor.flags = DoRed | DoGreen | DoBlue; xcolor.red = rgb.red[a]; xcolor.green = rgb.grn[a]; xcolor.blue = rgb.blu[a]; rc = XAllocColor(disp, *cmapP, &xcolor); if (rc == 0) { if ((visualP->class == StaticColor) || (visualP->class == StaticGray) || (visualP->class == TrueColor) || (visualP->class == DirectColor)) { pm_error("XAllocColor failed on a static visual"); } 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, *cmapP, colorIndex, a, 0); newmap = TRUE; break; } } colorIndex[a] = xcolor.pixel; } } else { newmap = TRUE; fit = FALSE; } if (newmap) { /* Either create a new colormap or fit the image into the one we have. To create a new one, we create a private cmap and allocate the colors writable. Fitting the colors is harder; we have to: 1. grab the server so no one can goof with the colormap. 2. count the available colors using XAllocColorCells. 3. free the colors we just allocated. 4. reduce the depth of the image to fit. 5. allocate the colors again shareable. 6. ungrab the server and continue on our way. Someone should shoot the people who designed X color allocation. */ unsigned int a; if (fit) { if (verbose) pm_message("Fitting image into default colormap"); XGrabServer(disp); } else { if (verbose) pm_message("Using private colormap"); /* create new colormap */ *cmapP = XCreateColormap(disp, RootWindow(disp, scrn), visualP, AllocNone); } allocColorCells(disp, *cmapP, colorIndex, rgb.used, &a); if (fit) { if (a > 0) XFreeColors(disp, *cmapP, colorIndex, a, 0); if (a <= 2) pm_error("Cannot fit into default colormap"); } if (a == 0) pm_error("Color allocation failed!"); if (fit) { unsigned int a; for (a = 0; a < rgb.used; ++a) { XColor xcolor; xcolor.flags = DoRed | DoGreen | DoBlue; xcolor.red = rgb.red[a]; xcolor.green = rgb.grn[a]; xcolor.blue = rgb.blu[a]; if (!XAllocColor(disp, *cmapP, &xcolor)) pm_error("XAllocColor failed while fitting colormap!"); colorIndex[a] = xcolor.pixel; } XUngrabServer(disp); } else { unsigned int b; for (b = 0; b < a; ++b) { XColor xcolor; xcolor.flags = DoRed | DoGreen | DoBlue; xcolor.pixel = colorIndex[b]; xcolor.red = rgb.red[b]; xcolor.green = rgb.grn[b]; xcolor.blue = rgb.blu[b]; XStoreColor(disp, *cmapP, &xcolor); } } } *colorIndexP = colorIndex; } static void doColorAllocation(XImageInfo * const ximageinfoP, Display * const disp, int const scrn, Visual * const visualP, Image * const imageP, bool const userWantsPrivateCmap, bool const userWantsFit, bool const verbose, Pixel ** const colorIndexP, Pixel ** const redvalP, Pixel ** const grnvalP, Pixel ** const bluvalP) { if ((visualP->class == TrueColor || visualP->class == DirectColor) && !BITMAPP(imageP)) { makeColorMap1(disp, scrn, visualP, &ximageinfoP->cmap, redvalP, grnvalP, bluvalP); *colorIndexP = NULL; } else { makeColorMap2(disp, scrn, visualP, imageP->rgb, userWantsPrivateCmap, userWantsFit, verbose, &ximageinfoP->cmap, colorIndexP); *redvalP = *grnvalP = *bluvalP = NULL; } } static void makeXImage(XImageInfo * const ximageinfoP, Display * const disp, int const scrn, Visual * const visualP, unsigned int const ddepth, Image * const imageP, Pixel const colorIndex[], Pixel const redvalue[], Pixel const grnvalue[], Pixel const bluvalue[], bool const verbose) { /*---------------------------------------------------------------------------- Create an XImage and related colormap based on the image type we have. -----------------------------------------------------------------------------*/ if (verbose) pm_message("Building XImage..."); switch (imageP->type) { case IBITMAP: { unsigned int const byteCount = (imageP->width + 7) / 8 * imageP->height; unsigned char * data; /* we copy the data to be more consistent */ MALLOCARRAY(data, byteCount); if (data == NULL) pm_error("Can't allocate space for %u byte image", byteCount); memcpy(data, imageP->data, byteCount); ximageinfoP->ximageP = XCreateImage(disp, visualP, 1, XYBitmap, 0, (char*)data, imageP->width, imageP->height, 8, 0); ximageinfoP->depth = ddepth; ximageinfoP->foreground = colorIndex[1]; ximageinfoP->background = colorIndex[0]; ximageinfoP->ximageP->bitmap_bit_order = MSBFirst; ximageinfoP->ximageP->byte_order = MSBFirst; } break; case IRGB: case ITRUE: { /* Modify image data to match visual and colormap */ unsigned int const dbits = bitsPerPixelAtDepth(disp, scrn, ddepth); unsigned int const dpixlen = (dbits + 7) / 8; ximageinfoP->depth = ddepth; switch (visualP->class) { case DirectColor: case TrueColor: { unsigned char * data; unsigned char * destptr; unsigned char * srcptr; ximageinfoP->ximageP = XCreateImage(disp, visualP, ddepth, ZPixmap, 0, NULL, imageP->width, imageP->height, 8, 0); MALLOCARRAY(data, imageP->width * imageP->height * dpixlen); if (data == NULL) pm_error("Unable to allocate space for %u x %u x %u image", imageP->width, imageP->height, dpixlen); ximageinfoP->ximageP->data = (char*)data; destptr = data; srcptr = imageP->data; switch (imageP->type) { case ITRUE: { unsigned int y; for (y= 0; y < imageP->height; ++y) { unsigned int x; for (x= 0; x < imageP->width; ++x) { Pixel const pixval = memToVal(srcptr, imageP->pixlen); Pixel const newpixval = redvalue[TRUE_RED(pixval)] | grnvalue[TRUE_GRN(pixval)] | bluvalue[TRUE_BLU(pixval)]; valToMem(newpixval, destptr, dpixlen); srcptr += imageP->pixlen; destptr += dpixlen; } } } break; case IRGB: { unsigned int y; for (y= 0; y < imageP->height; ++y) { unsigned int x; for (x = 0; x < imageP->width; ++x) { Pixel const pixval = memToVal(srcptr, imageP->pixlen); Pixel const newpixval = redvalue[imageP->rgb.red[pixval] >> 8] | grnvalue[imageP->rgb.grn[pixval] >> 8] | bluvalue[imageP->rgb.blu[pixval] >> 8]; valToMem(newpixval, destptr, dpixlen); srcptr += imageP->pixlen; destptr += dpixlen; } } } break; default: /* something's broken */ pm_error("INTERNAL ERROR: Unexpected image type %u for " "DirectColor/TrueColor visual!", imageP->type); } ximageinfoP->ximageP->byte_order = MSBFirst; /* Trust me, I know what I'm talking about */ } break; default: { /* only IRGB images make it this far. */ /* If our XImage doesn't have modulus 8 bits per pixel, it's unclear how to pack bits so we instead use an XYPixmap image. This is slower. */ if (dbits % 8) { unsigned int const linelen = (imageP->width + 7) / 8; unsigned int const size = imageP->width * imageP->height * dpixlen; unsigned char * data; unsigned char * destptr; unsigned char * srcptr; Pixel pixval; unsigned int a; ximageinfoP->ximageP = XCreateImage(disp, visualP, ddepth, XYPixmap, 0, NULL, imageP->width, imageP->height, 8, 0); MALLOCARRAY(data, size); if (data == NULL) pm_error("Unable to allocate space for %u x %x x %u " "image", imageP->width, imageP->height, dpixlen); ximageinfoP->ximageP->data = (char*)data; memset(data, 0, size); ximageinfoP->ximageP->bitmap_bit_order = MSBFirst; ximageinfoP->ximageP->byte_order = MSBFirst; for (a= 0; a < dbits; ++a) { Pixel const pixmask = 1 << a; unsigned char * const destdata = data + ((ddepth - a - 1) * imageP->height * linelen); unsigned int y; srcptr = imageP->data; for (y= 0; y < imageP->height; ++y) { unsigned int x; unsigned char mask; destptr = destdata + (y * linelen); *destptr = 0; mask = 0x80; for (x= 0; x < imageP->width; ++x) { pixval = memToVal(srcptr, imageP->pixlen); srcptr += imageP->pixlen; if (colorIndex[pixval] & pixmask) *destptr |= mask; mask >>= 1; if (mask == 0) { mask = 0x80; ++destptr; } } } } } else { unsigned int const dpixlen = (ximageinfoP->ximageP->bits_per_pixel + 7) / 8; unsigned char * data; unsigned char * srcptr; unsigned char * destptr; unsigned int y; ximageinfoP->ximageP = XCreateImage(disp, visualP, ddepth, ZPixmap, 0, NULL, imageP->width, imageP->height, 8, 0); MALLOCARRAY(data, imageP->width * imageP->height * dpixlen); if (data == NULL) pm_error("Failed to allocate space for %u x %u x %u image", imageP->width, imageP->height, dpixlen); ximageinfoP->ximageP->data = (char*)data; ximageinfoP->ximageP->byte_order= MSBFirst; /* Trust me, i know what I'm talking about */ srcptr = imageP->data; destptr = data; for (y= 0; y < imageP->height; ++y) { unsigned int x; for (x= 0; x < imageP->width; x++) { valToMem(colorIndex[memToVal(srcptr, imageP->pixlen)], destptr, dpixlen); srcptr += imageP->pixlen; destptr += dpixlen; } } } } break; } } break; } if (verbose) pm_message("done"); } XImageInfo * imageToXImage(Display * const disp, int const scrn, Visual * const visualP, /* visual to use */ unsigned int const ddepth, /* depth of the visual to use */ Image * const origImageP, bool const userWantsPrivateCmap, bool const userWantsFit, bool const verbose) { XImageInfo * ximageinfoP; Image * imageP; Pixel * colorIndex; Pixel * redvalue; Pixel * grnvalue; Pixel * bluvalue; assertGoodImage(origImageP); MALLOCVAR_NOFAIL(ximageinfoP); ximageinfoP->disp = disp; ximageinfoP->scrn = scrn; ximageinfoP->depth = 0; ximageinfoP->drawable = None; ximageinfoP->foreground = ximageinfoP->background = 0; ximageinfoP->gc = NULL; ximageinfoP->ximageP = NULL; makeUsableVisual(origImageP, visualP, ddepth, &imageP); assertGoodImage(imageP); doColorAllocation(ximageinfoP, disp, scrn, visualP, imageP, userWantsPrivateCmap, userWantsFit, verbose, &colorIndex, &redvalue, &grnvalue, &bluvalue); makeXImage(ximageinfoP, disp, scrn, visualP, ddepth, imageP, colorIndex, redvalue, grnvalue, bluvalue, verbose); if (colorIndex) free(colorIndex); if (redvalue) { free(redvalue); free(grnvalue); free(bluvalue); } if (imageP != origImageP) freeImage(imageP); return ximageinfoP; } void sendXImage(XImageInfo * const ximageinfoP, int const src_x, int const src_y, int const dst_x, int const dst_y, unsigned int const w, unsigned int const h) { /*---------------------------------------------------------------------------- Given an XImage and a drawable, move a rectangle from the Ximage to the drawable. -----------------------------------------------------------------------------*/ XGCValues gcv; /* build and cache the GC */ if (!ximageinfoP->gc) { gcv.function = GXcopy; if (ximageinfoP->ximageP->depth == 1) { gcv.foreground = ximageinfoP->foreground; gcv.background= ximageinfoP->background; ximageinfoP->gc = XCreateGC(ximageinfoP->disp, ximageinfoP->drawable, GCFunction | GCForeground | GCBackground, &gcv); }else ximageinfoP->gc = XCreateGC(ximageinfoP->disp, ximageinfoP->drawable, GCFunction, &gcv); } XPutImage(ximageinfoP->disp, ximageinfoP->drawable, ximageinfoP->gc, ximageinfoP->ximageP, src_x, src_y, dst_x, dst_y, w, h); } void freeXImage(Image * const imageP, XImageInfo * const ximageinfoP) { /*---------------------------------------------------------------------------- free up anything cached in the local Ximage structure. -----------------------------------------------------------------------------*/ if (ximageinfoP->gc) XFreeGC(ximageinfoP->disp, ximageinfoP->gc); free(ximageinfoP->ximageP->data); ximageinfoP->ximageP->data = NULL; XDestroyImage(ximageinfoP->ximageP); free(ximageinfoP); }