/* convert.c - convert RImage to Pixmap * * Raster graphics library * * Copyright (c) 1997-2003 Alfredo K. Kojima * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, * MA 02110-1301, USA. */ /* Problems: * 1. Using Grayscale visual with Dithering crashes wmaker * 2. Ghost dock/appicon is wrong in Pseudocolor, Staticgray, Grayscale */ #include #include #include #include #include #include #include #include "wraster.h" #include "convert.h" #ifdef USE_XSHM extern Pixmap R_CreateXImageMappedPixmap(RContext * context, RXImage * ximage); #endif #define NFREE(n) if (n) free(n) #define HAS_ALPHA(I) ((I)->format == RRGBAFormat) typedef struct RConversionTable { unsigned short table[256]; unsigned short index; struct RConversionTable *next; } RConversionTable; typedef struct RStdConversionTable { unsigned int table[256]; unsigned short mult; unsigned short max; struct RStdConversionTable *next; } RStdConversionTable; static RConversionTable *conversionTable = NULL; static RStdConversionTable *stdConversionTable = NULL; static void release_conversion_table(void) { RConversionTable *tmp = conversionTable; while (tmp) { RConversionTable *tmp_to_delete = tmp; tmp = tmp->next; free(tmp_to_delete); } conversionTable = NULL; } static void release_std_conversion_table(void) { RStdConversionTable *tmp = stdConversionTable; while (tmp) { RStdConversionTable *tmp_to_delete = tmp; tmp = tmp->next; free(tmp_to_delete); } stdConversionTable = NULL; } void r_destroy_conversion_tables(void) { release_conversion_table(); release_std_conversion_table(); } static unsigned short *computeTable(unsigned short mask) { RConversionTable *tmp = conversionTable; int i; while (tmp) { if (tmp->index == mask) break; tmp = tmp->next; } if (tmp) return tmp->table; tmp = (RConversionTable *) malloc(sizeof(RConversionTable)); if (tmp == NULL) return NULL; for (i = 0; i < 256; i++) tmp->table[i] = (i * mask + 0x7f) / 0xff; tmp->index = mask; tmp->next = conversionTable; conversionTable = tmp; return tmp->table; } static unsigned int *computeStdTable(unsigned int mult, unsigned int max) { RStdConversionTable *tmp = stdConversionTable; unsigned int i; while (tmp) { if (tmp->mult == mult && tmp->max == max) break; tmp = tmp->next; } if (tmp) return tmp->table; tmp = (RStdConversionTable *) malloc(sizeof(RStdConversionTable)); if (tmp == NULL) return NULL; for (i = 0; i < 256; i++) { tmp->table[i] = (i * max) / 0xff * mult; } tmp->mult = mult; tmp->max = max; tmp->next = stdConversionTable; stdConversionTable = tmp; return tmp->table; } /***************************************************************************/ static void convertTrueColor_generic(RXImage * ximg, RImage * image, signed char *err, signed char *nerr, const unsigned short *rtable, const unsigned short *gtable, const unsigned short *btable, const int dr, const int dg, const int db, const unsigned short roffs, const unsigned short goffs, const unsigned short boffs) { signed char *terr; int x, y, r, g, b; int pixel; int rer, ger, ber; unsigned char *ptr = image->data; int channels = (HAS_ALPHA(image) ? 4 : 3); /* convert and dither the image to XImage */ for (y = 0; y < image->height; y++) { nerr[0] = 0; nerr[1] = 0; nerr[2] = 0; for (x = 0; x < image->width; x++, ptr += channels) { /* reduce pixel */ pixel = *ptr + err[x]; if (pixel < 0) pixel = 0; else if (pixel > 0xff) pixel = 0xff; r = rtable[pixel]; /* calc error */ rer = pixel - r * dr; /* reduce pixel */ pixel = *(ptr + 1) + err[x + 1]; if (pixel < 0) pixel = 0; else if (pixel > 0xff) pixel = 0xff; g = gtable[pixel]; /* calc error */ ger = pixel - g * dg; /* reduce pixel */ pixel = *(ptr + 2) + err[x + 2]; if (pixel < 0) pixel = 0; else if (pixel > 0xff) pixel = 0xff; b = btable[pixel]; /* calc error */ ber = pixel - b * db; pixel = (r << roffs) | (g << goffs) | (b << boffs); XPutPixel(ximg->image, x, y, pixel); /* distribute error */ r = (rer * 3) / 8; g = (ger * 3) / 8; b = (ber * 3) / 8; /* x+1, y */ err[x + 3 * 1] += r; err[x + 1 + 3 * 1] += g; err[x + 2 + 3 * 1] += b; /* x, y+1 */ nerr[x] += r; nerr[x + 1] += g; nerr[x + 2] += b; /* x+1, y+1 */ nerr[x + 3 * 1] = rer - 2 * r; nerr[x + 1 + 3 * 1] = ger - 2 * g; nerr[x + 2 + 3 * 1] = ber - 2 * b; } /* skip to next line */ terr = err; err = nerr; nerr = terr; } /* redither the 1st line to distribute error better */ ptr = image->data; y = 0; nerr[0] = 0; nerr[1] = 0; nerr[2] = 0; for (x = 0; x < image->width; x++, ptr += channels) { /* reduce pixel */ pixel = *ptr + err[x]; if (pixel < 0) pixel = 0; else if (pixel > 0xff) pixel = 0xff; r = rtable[pixel]; /* calc error */ rer = pixel - r * dr; /* reduce pixel */ pixel = *(ptr + 1) + err[x + 1]; if (pixel < 0) pixel = 0; else if (pixel > 0xff) pixel = 0xff; g = gtable[pixel]; /* calc error */ ger = pixel - g * dg; /* reduce pixel */ pixel = *(ptr + 2) + err[x + 2]; if (pixel < 0) pixel = 0; else if (pixel > 0xff) pixel = 0xff; b = btable[pixel]; /* calc error */ ber = pixel - b * db; pixel = (r << roffs) | (g << goffs) | (b << boffs); XPutPixel(ximg->image, x, y, pixel); /* distribute error */ r = (rer * 3) / 8; g = (ger * 3) / 8; b = (ber * 3) / 8; /* x+1, y */ err[x + 3 * 1] += r; err[x + 1 + 3 * 1] += g; err[x + 2 + 3 * 1] += b; /* x, y+1 */ nerr[x] += r; nerr[x + 1] += g; nerr[x + 2] += b; /* x+1, y+1 */ nerr[x + 3 * 1] = rer - 2 * r; nerr[x + 1 + 3 * 1] = ger - 2 * g; nerr[x + 2 + 3 * 1] = ber - 2 * b; } } static RXImage *image2TrueColor(RContext * ctx, RImage * image) { RXImage *ximg; unsigned short rmask, gmask, bmask; unsigned short roffs, goffs, boffs; unsigned short *rtable, *gtable, *btable; int channels = (HAS_ALPHA(image) ? 4 : 3); ximg = RCreateXImage(ctx, ctx->depth, image->width, image->height); if (!ximg) { return NULL; } roffs = ctx->red_offset; goffs = ctx->green_offset; boffs = ctx->blue_offset; rmask = ctx->visual->red_mask >> roffs; gmask = ctx->visual->green_mask >> goffs; bmask = ctx->visual->blue_mask >> boffs; rtable = computeTable(rmask); gtable = computeTable(gmask); btable = computeTable(bmask); if (rtable == NULL || gtable == NULL || btable == NULL) { RErrorCode = RERR_NOMEMORY; RDestroyXImage(ctx, ximg); return NULL; } if (ctx->attribs->render_mode == RBestMatchRendering) { int ofs, r, g, b; int x, y; unsigned long pixel; unsigned char *ptr = image->data; /* fake match */ #ifdef WRLIB_DEBUG fputs("true color match\n", stderr); #endif if (rmask == 0xff && gmask == 0xff && bmask == 0xff) { for (y = 0; y < image->height; y++) { for (x = 0; x < image->width; x++, ptr += channels) { /* reduce pixel */ pixel = (*(ptr) << roffs) | (*(ptr + 1) << goffs) | (*(ptr + 2) << boffs); XPutPixel(ximg->image, x, y, pixel); } } } else { for (y = 0, ofs = 0; y < image->height; y++) { for (x = 0; x < image->width; x++, ofs += channels - 3) { /* reduce pixel */ r = rtable[ptr[ofs++]]; g = gtable[ptr[ofs++]]; b = btable[ptr[ofs++]]; pixel = (r << roffs) | (g << goffs) | (b << boffs); XPutPixel(ximg->image, x, y, pixel); } } } } else { /* dither */ const int dr = 0xff / rmask; const int dg = 0xff / gmask; const int db = 0xff / bmask; #ifdef WRLIB_DEBUG fputs("true color dither\n", stderr); #endif { signed char *err; signed char *nerr; int ch = (HAS_ALPHA(image) ? 4 : 3); err = malloc(ch * (image->width + 2)); nerr = malloc(ch * (image->width + 2)); if (!err || !nerr) { NFREE(err); NFREE(nerr); RErrorCode = RERR_NOMEMORY; RDestroyXImage(ctx, ximg); return NULL; } memset(err, 0, ch * (image->width + 2)); memset(nerr, 0, ch * (image->width + 2)); convertTrueColor_generic(ximg, image, err, nerr, rtable, gtable, btable, dr, dg, db, roffs, goffs, boffs); free(err); free(nerr); } } return ximg; } /***************************************************************************/ static void convertPseudoColor_to_8(RXImage * ximg, RImage * image, signed char *err, signed char *nerr, const unsigned short *rtable, const unsigned short *gtable, const unsigned short *btable, const int dr, const int dg, const int db, unsigned long *pixels, int cpc) { signed char *terr; int x, y, r, g, b; int pixel; int rer, ger, ber; unsigned char *ptr = image->data; unsigned char *optr = (unsigned char *)ximg->image->data; int channels = (HAS_ALPHA(image) ? 4 : 3); int cpcpc = cpc * cpc; /* convert and dither the image to XImage */ for (y = 0; y < image->height; y++) { nerr[0] = 0; nerr[1] = 0; nerr[2] = 0; for (x = 0; x < image->width * 3; x += 3, ptr += channels) { /* reduce pixel */ pixel = *ptr + err[x]; if (pixel < 0) pixel = 0; else if (pixel > 0xff) pixel = 0xff; r = rtable[pixel]; /* calc error */ rer = pixel - r * dr; /* reduce pixel */ pixel = *(ptr + 1) + err[x + 1]; if (pixel < 0) pixel = 0; else if (pixel > 0xff) pixel = 0xff; g = gtable[pixel]; /* calc error */ ger = pixel - g * dg; /* reduce pixel */ pixel = *(ptr + 2) + err[x + 2]; if (pixel < 0) pixel = 0; else if (pixel > 0xff) pixel = 0xff; b = btable[pixel]; /* calc error */ ber = pixel - b * db; *optr++ = pixels[r * cpcpc + g * cpc + b]; /* distribute error */ r = (rer * 3) / 8; g = (ger * 3) / 8; b = (ber * 3) / 8; /* x+1, y */ err[x + 3 * 1] += r; err[x + 1 + 3 * 1] += g; err[x + 2 + 3 * 1] += b; /* x, y+1 */ nerr[x] += r; nerr[x + 1] += g; nerr[x + 2] += b; /* x+1, y+1 */ nerr[x + 3 * 1] = rer - 2 * r; nerr[x + 1 + 3 * 1] = ger - 2 * g; nerr[x + 2 + 3 * 1] = ber - 2 * b; } /* skip to next line */ terr = err; err = nerr; nerr = terr; optr += ximg->image->bytes_per_line - image->width; } } static RXImage *image2PseudoColor(RContext * ctx, RImage * image) { RXImage *ximg; register int x, y, r, g, b; unsigned char *ptr; unsigned long pixel; const int cpc = ctx->attribs->colors_per_channel; const unsigned short rmask = cpc - 1; /* different sizes could be used */ const unsigned short gmask = rmask; /* for r,g,b */ const unsigned short bmask = rmask; unsigned short *rtable, *gtable, *btable; const int cpccpc = cpc * cpc; int channels = (HAS_ALPHA(image) ? 4 : 3); ximg = RCreateXImage(ctx, ctx->depth, image->width, image->height); if (!ximg) { return NULL; } ptr = image->data; /* Tables are same at the moment because rmask==gmask==bmask. */ rtable = computeTable(rmask); gtable = computeTable(gmask); btable = computeTable(bmask); if (rtable == NULL || gtable == NULL || btable == NULL) { RErrorCode = RERR_NOMEMORY; RDestroyXImage(ctx, ximg); return NULL; } if (ctx->attribs->render_mode == RBestMatchRendering) { /* fake match */ #ifdef WRLIB_DEBUG fprintf(stderr, "pseudo color match with %d colors per channel\n", cpc); #endif for (y = 0; y < image->height; y++) { for (x = 0; x < image->width; x++, ptr += channels - 3) { /* reduce pixel */ r = rtable[*ptr++]; g = gtable[*ptr++]; b = btable[*ptr++]; pixel = r * cpccpc + g * cpc + b; /*data[ofs] = ctx->colors[pixel].pixel; */ XPutPixel(ximg->image, x, y, ctx->colors[pixel].pixel); } } } else { /* dither */ signed char *err; signed char *nerr; const int dr = 0xff / rmask; const int dg = 0xff / gmask; const int db = 0xff / bmask; #ifdef WRLIB_DEBUG fprintf(stderr, "pseudo color dithering with %d colors per channel\n", cpc); #endif err = malloc(4 * (image->width + 3)); nerr = malloc(4 * (image->width + 3)); if (!err || !nerr) { NFREE(err); NFREE(nerr); RErrorCode = RERR_NOMEMORY; RDestroyXImage(ctx, ximg); return NULL; } memset(err, 0, 4 * (image->width + 3)); memset(nerr, 0, 4 * (image->width + 3)); convertPseudoColor_to_8(ximg, image, err + 4, nerr + 4, rtable, gtable, btable, dr, dg, db, ctx->pixels, cpc); free(err); free(nerr); } return ximg; } /* * For standard colormap */ static RXImage *image2StandardPseudoColor(RContext * ctx, RImage * image) { RXImage *ximg; register int x, y, r, g, b; unsigned char *ptr; unsigned long pixel; unsigned char *data; unsigned int *rtable, *gtable, *btable; unsigned int base_pixel = ctx->std_rgb_map->base_pixel; int channels = (HAS_ALPHA(image) ? 4 : 3); ximg = RCreateXImage(ctx, ctx->depth, image->width, image->height); if (!ximg) { return NULL; } ptr = image->data; data = (unsigned char *)ximg->image->data; rtable = computeStdTable(ctx->std_rgb_map->red_mult, ctx->std_rgb_map->red_max); gtable = computeStdTable(ctx->std_rgb_map->green_mult, ctx->std_rgb_map->green_max); btable = computeStdTable(ctx->std_rgb_map->blue_mult, ctx->std_rgb_map->blue_max); if (rtable == NULL || gtable == NULL || btable == NULL) { RErrorCode = RERR_NOMEMORY; RDestroyXImage(ctx, ximg); return NULL; } if (ctx->attribs->render_mode == RBestMatchRendering) { for (y = 0; y < image->height; y++) { for (x = 0; x < image->width; x++, ptr += channels) { /* reduce pixel */ pixel = (rtable[*ptr] + gtable[*(ptr + 1)] + btable[*(ptr + 2)] + base_pixel) & 0xffffffff; XPutPixel(ximg->image, x, y, pixel); } } } else { /* dither */ signed short *err, *nerr; signed short *terr; int rer, ger, ber; int x1, ofs; #ifdef WRLIB_DEBUG fprintf(stderr, "pseudo color dithering with %d colors per channel\n", ctx->attribs->colors_per_channel); #endif err = (short *)malloc(3 * (image->width + 2) * sizeof(short)); nerr = (short *)malloc(3 * (image->width + 2) * sizeof(short)); if (!err || !nerr) { NFREE(err); NFREE(nerr); RErrorCode = RERR_NOMEMORY; RDestroyXImage(ctx, ximg); return NULL; } for (x = 0, x1 = 0; x < image->width * 3; x1 += channels - 3) { err[x++] = ptr[x1++]; err[x++] = ptr[x1++]; err[x++] = ptr[x1++]; } err[x] = err[x + 1] = err[x + 2] = 0; /* convert and dither the image to XImage */ for (y = 0, ofs = 0; y < image->height; y++) { if (y < image->height - 1) { int x1; for (x = 0, x1 = (y + 1) * image->width * channels; x < image->width * 3; x1 += channels - 3) { nerr[x++] = ptr[x1++]; nerr[x++] = ptr[x1++]; nerr[x++] = ptr[x1++]; } /* last column */ x1 -= channels; nerr[x++] = ptr[x1++]; nerr[x++] = ptr[x1++]; nerr[x++] = ptr[x1++]; } for (x = 0; x < image->width * 3; x += 3, ofs++) { /* reduce pixel */ if (err[x] > 0xff) err[x] = 0xff; else if (err[x] < 0) err[x] = 0; if (err[x + 1] > 0xff) err[x + 1] = 0xff; else if (err[x + 1] < 0) err[x + 1] = 0; if (err[x + 2] > 0xff) err[x + 2] = 0xff; else if (err[x + 2] < 0) err[x + 2] = 0; r = rtable[err[x]]; g = gtable[err[x + 1]]; b = btable[err[x + 2]]; pixel = r + g + b; data[ofs] = base_pixel + pixel; /* calc error */ rer = err[x] - (ctx->colors[pixel].red >> 8); ger = err[x + 1] - (ctx->colors[pixel].green >> 8); ber = err[x + 2] - (ctx->colors[pixel].blue >> 8); /* distribute error */ err[x + 3 * 1] += (rer * 7) / 16; err[x + 1 + 3 * 1] += (ger * 7) / 16; err[x + 2 + 3 * 1] += (ber * 7) / 16; nerr[x] += (rer * 5) / 16; nerr[x + 1] += (ger * 5) / 16; nerr[x + 2] += (ber * 5) / 16; if (x > 0) { nerr[x - 3 * 1] += (rer * 3) / 16; nerr[x - 3 * 1 + 1] += (ger * 3) / 16; nerr[x - 3 * 1 + 2] += (ber * 3) / 16; } nerr[x + 3 * 1] += rer / 16; nerr[x + 1 + 3 * 1] += ger / 16; nerr[x + 2 + 3 * 1] += ber / 16; } /* skip to next line */ terr = err; err = nerr; nerr = terr; ofs += ximg->image->bytes_per_line - image->width; } free(err); free(nerr); } ximg->image->data = (char *)data; return ximg; } static RXImage *image2GrayScale(RContext * ctx, RImage * image) { RXImage *ximg; register int x, y, g; unsigned char *ptr; const int cpc = ctx->attribs->colors_per_channel; unsigned short gmask; unsigned short *table; unsigned char *data; int channels = (HAS_ALPHA(image) ? 4 : 3); ximg = RCreateXImage(ctx, ctx->depth, image->width, image->height); if (!ximg) { return NULL; } ptr = image->data; data = (unsigned char *)ximg->image->data; if (ctx->vclass == StaticGray) gmask = (1 << ctx->depth) - 1; /* use all grays */ else gmask = cpc * cpc * cpc - 1; table = computeTable(gmask); if (table == NULL) { RErrorCode = RERR_NOMEMORY; RDestroyXImage(ctx, ximg); return NULL; } if (ctx->attribs->render_mode == RBestMatchRendering) { /* fake match */ #ifdef WRLIB_DEBUG fprintf(stderr, "grayscale match with %d colors per channel\n", cpc); #endif for (y = 0; y < image->height; y++) { for (x = 0; x < image->width; x++) { /* reduce pixel */ g = table[(*ptr * 30 + *(ptr + 1) * 59 + *(ptr + 2) * 11) / 100]; ptr += channels; /*data[ofs] = ctx->colors[g].pixel; */ XPutPixel(ximg->image, x, y, ctx->colors[g].pixel); } } } else { /* dither */ short *gerr; short *ngerr; short *terr; int ger; const int dg = 0xff / gmask; #ifdef WRLIB_DEBUG fprintf(stderr, "grayscale dither with %d colors per channel\n", cpc); #endif gerr = (short *)malloc((image->width + 2) * sizeof(short)); ngerr = (short *)malloc((image->width + 2) * sizeof(short)); if (!gerr || !ngerr) { NFREE(gerr); NFREE(ngerr); RErrorCode = RERR_NOMEMORY; RDestroyXImage(ctx, ximg); return NULL; } for (x = 0, y = 0; x < image->width; x++, y += channels) { gerr[x] = (ptr[y] * 30 + ptr[y + 1] * 59 + ptr[y + 2] * 11) / 100; } gerr[x] = 0; /* convert and dither the image to XImage */ for (y = 0; y < image->height; y++) { if (y < image->height - 1) { int x1; for (x = 0, x1 = (y + 1) * image->width * channels; x < image->width; x++, x1 += channels) { ngerr[x] = (ptr[x1] * 30 + ptr[x1 + 1] * 59 + ptr[x1 + 2] * 11) / 100; } /* last column */ x1 -= channels; ngerr[x] = (ptr[x1] * 30 + ptr[x1 + 1] * 59 + ptr[x1 + 2] * 11) / 100; } for (x = 0; x < image->width; x++) { /* reduce pixel */ if (gerr[x] > 0xff) gerr[x] = 0xff; else if (gerr[x] < 0) gerr[x] = 0; g = table[gerr[x]]; /*data[ofs] = ctx->colors[g].pixel; */ XPutPixel(ximg->image, x, y, ctx->colors[g].pixel); /* calc error */ ger = gerr[x] - g * dg; /* distribute error */ g = (ger * 3) / 8; /* x+1, y */ gerr[x + 1] += g; /* x, y+1 */ ngerr[x] += g; /* x+1, y+1 */ ngerr[x + 1] += ger - 2 * g; } /* skip to next line */ terr = gerr; gerr = ngerr; ngerr = terr; } free(gerr); free(ngerr); } ximg->image->data = (char *)data; return ximg; } static RXImage *image2Bitmap(RContext * ctx, RImage * image, int threshold) { RXImage *ximg; unsigned char *alpha; int x, y; ximg = RCreateXImage(ctx, 1, image->width, image->height); if (!ximg) { return NULL; } alpha = image->data + 3; for (y = 0; y < image->height; y++) { for (x = 0; x < image->width; x++) { XPutPixel(ximg->image, x, y, (*alpha <= threshold ? 0 : 1)); alpha += 4; } } return ximg; } int RConvertImage(RContext * context, RImage * image, Pixmap * pixmap) { RXImage *ximg = NULL; #ifdef USE_XSHM Pixmap tmp; #endif assert(context != NULL); assert(image != NULL); assert(pixmap != NULL); switch (context->vclass) { case TrueColor: ximg = image2TrueColor(context, image); break; case PseudoColor: case StaticColor: if (context->attribs->standard_colormap_mode != RIgnoreStdColormap) ximg = image2StandardPseudoColor(context, image); else ximg = image2PseudoColor(context, image); break; case GrayScale: case StaticGray: ximg = image2GrayScale(context, image); break; } if (!ximg) { return False; } *pixmap = XCreatePixmap(context->dpy, context->drawable, image->width, image->height, context->depth); #ifdef USE_XSHM if (context->flags.use_shared_pixmap && ximg->is_shared) tmp = R_CreateXImageMappedPixmap(context, ximg); else tmp = None; if (tmp) { /* * We have to copy the shm Pixmap into a normal Pixmap because * otherwise, we would have to control when Pixmaps are freed so * that we can detach their shm segments. This is a problem if the * program crash, leaving stale shared memory segments in the * system (lots of them). But with some work, we can optimize * things and remove this XCopyArea. This will require * explicitly freeing all pixmaps when exiting or restarting * wmaker. */ XCopyArea(context->dpy, tmp, *pixmap, context->copy_gc, 0, 0, image->width, image->height, 0, 0); XFreePixmap(context->dpy, tmp); } else { RPutXImage(context, *pixmap, context->copy_gc, ximg, 0, 0, 0, 0, image->width, image->height); } #else /* !USE_XSHM */ RPutXImage(context, *pixmap, context->copy_gc, ximg, 0, 0, 0, 0, image->width, image->height); #endif /* !USE_XSHM */ RDestroyXImage(context, ximg); return True; } /* make the gc permanent (create with context creation). * GC creation is very expensive. altering its properties is not. -Dan */ int RConvertImageMask(RContext * context, RImage * image, Pixmap * pixmap, Pixmap * mask, int threshold) { GC gc; XGCValues gcv; RXImage *ximg = NULL; assert(context != NULL); assert(image != NULL); assert(pixmap != NULL); assert(mask != NULL); if (!RConvertImage(context, image, pixmap)) return False; if (image->format == RRGBFormat) { *mask = None; return True; } ximg = image2Bitmap(context, image, threshold); if (!ximg) { return False; } *mask = XCreatePixmap(context->dpy, context->drawable, image->width, image->height, 1); gcv.foreground = context->black; gcv.background = context->white; gcv.graphics_exposures = False; gc = XCreateGC(context->dpy, *mask, GCForeground | GCBackground | GCGraphicsExposures, &gcv); RPutXImage(context, *mask, gc, ximg, 0, 0, 0, 0, image->width, image->height); RDestroyXImage(context, ximg); XFreeGC(context->dpy, gc); return True; } Bool RGetClosestXColor(RContext * context, const RColor * color, XColor * retColor) { if (context->vclass == TrueColor) { unsigned short rmask, gmask, bmask; unsigned short roffs, goffs, boffs; unsigned short *rtable, *gtable, *btable; roffs = context->red_offset; goffs = context->green_offset; boffs = context->blue_offset; rmask = context->visual->red_mask >> roffs; gmask = context->visual->green_mask >> goffs; bmask = context->visual->blue_mask >> boffs; rtable = computeTable(rmask); gtable = computeTable(gmask); btable = computeTable(bmask); retColor->pixel = (rtable[color->red] << roffs) | (gtable[color->green] << goffs) | (btable[color->blue] << boffs); retColor->red = color->red << 8; retColor->green = color->green << 8; retColor->blue = color->blue << 8; retColor->flags = DoRed | DoGreen | DoBlue; } else if (context->vclass == PseudoColor || context->vclass == StaticColor) { if (context->attribs->standard_colormap_mode != RIgnoreStdColormap) { unsigned int *rtable, *gtable, *btable; rtable = computeStdTable(context->std_rgb_map->red_mult, context->std_rgb_map->red_max); gtable = computeStdTable(context->std_rgb_map->green_mult, context->std_rgb_map->green_max); btable = computeStdTable(context->std_rgb_map->blue_mult, context->std_rgb_map->blue_max); if (rtable == NULL || gtable == NULL || btable == NULL) { RErrorCode = RERR_NOMEMORY; return False; } retColor->pixel = (rtable[color->red] + gtable[color->green] + btable[color->blue] + context->std_rgb_map->base_pixel) & 0xffffffff; retColor->red = color->red << 8; retColor->green = color->green << 8; retColor->blue = color->blue << 8; retColor->flags = DoRed | DoGreen | DoBlue; } else { const int cpc = context->attribs->colors_per_channel; const unsigned short rmask = cpc - 1; /* different sizes could be used */ const unsigned short gmask = rmask; /* for r,g,b */ const unsigned short bmask = rmask; unsigned short *rtable, *gtable, *btable; const int cpccpc = cpc * cpc; int index; rtable = computeTable(rmask); gtable = computeTable(gmask); btable = computeTable(bmask); if (rtable == NULL || gtable == NULL || btable == NULL) { RErrorCode = RERR_NOMEMORY; return False; } index = rtable[color->red] * cpccpc + gtable[color->green] * cpc + btable[color->blue]; *retColor = context->colors[index]; } } else if (context->vclass == GrayScale || context->vclass == StaticGray) { const int cpc = context->attribs->colors_per_channel; unsigned short gmask; unsigned short *table; int index; if (context->vclass == StaticGray) gmask = (1 << context->depth) - 1; /* use all grays */ else gmask = cpc * cpc * cpc - 1; table = computeTable(gmask); if (!table) return False; index = table[(color->red * 30 + color->green * 59 + color->blue * 11) / 100]; *retColor = context->colors[index]; } else { RErrorCode = RERR_INTERNAL; return False; } return True; }