X-Git-Url: https://repo.or.cz/w/wmaker-crm.git/blobdiff_plain/59fc927dc9f183802621138534fa6eaafe5593ba..688a56e8ab67b56550e2874d9d7423f0d435bfd9:/wrlib/convert.c diff --git a/wrlib/convert.c b/wrlib/convert.c dissimilarity index 91% index cd543047..46519e20 100644 --- a/wrlib/convert.c +++ b/wrlib/convert.c @@ -1,1150 +1,1115 @@ -/* 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., 675 Mass Ave, Cambridge, MA 02139, 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 - - -#ifdef BENCH -#include "bench.h" -#endif - -#include "wraster.h" - -#ifdef XSHM -extern Pixmap R_CreateXImageMappedPixmap(RContext *context, RXImage *ximage); - -#endif - - -#ifdef ASM_X86 -extern void x86_PseudoColor_32_to_8(unsigned char *image, - unsigned char *ximage, - char *err, char *nerr, - short *ctable, - int dr, int dg, int db, - unsigned long *pixels, - int cpc, - int width, int height, - int bytesPerPixel, - int line_offset); -#endif /* ASM_X86 */ - -#ifdef ASM_X86_MMX - -extern int x86_check_mmx(); - -extern void x86_mmx_TrueColor_32_to_16(unsigned char *image, - unsigned short *ximage, - short *err, short *nerr, - const unsigned short *rtable, - const unsigned short *gtable, - const unsigned short *btable, - int dr, int dg, int db, - unsigned int roffs, - unsigned int goffs, - unsigned int boffs, - int width, int height, - int line_offset); - - - -#endif /* ASM_X86_MMX */ - -#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 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; yheight; y++) { - nerr[0] = 0; - nerr[1] = 0; - nerr[2] = 0; - for (x=0; xwidth; 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<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; xwidth; 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<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; - } - - -#ifdef BENCH - cycle_bench(1); -#endif - - 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 DEBUG - puts("true color match"); -#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)<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<image, x, y, pixel); - } - } - } - } else { - /* dither */ - const int dr=0xff/rmask; - const int dg=0xff/gmask; - const int db=0xff/bmask; - -#ifdef DEBUG - puts("true color dither"); -#endif - -#ifdef ASM_X86_MMX - if (ctx->depth==16 && HAS_ALPHA(image) && x86_check_mmx()) { - short *err; - short *nerr; - - err = malloc(8*(image->width+3)); - nerr = malloc(8*(image->width+3)); - if (!err || !nerr) { - NFREE(err); - NFREE(nerr); - RErrorCode = RERR_NOMEMORY; - RDestroyXImage(ctx, ximg); - return NULL; - } - memset(err, 0, 8*(image->width+3)); - memset(nerr, 0, 8*(image->width+3)); - - x86_mmx_TrueColor_32_to_16(image->data, - (unsigned short*)ximg->image->data, - err+8, nerr+8, - rtable, gtable, btable, - dr, dg, db, - roffs, goffs, boffs, - image->width, image->height, - ximg->image->bytes_per_line - 2*image->width); - - free(err); - free(nerr); - } else -#endif /* ASM_X86_MMX */ - { - 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); - } - - } - -#ifdef BENCH - cycle_bench(0); -#endif - - 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; yheight; y++) { - nerr[0] = 0; - nerr[1] = 0; - nerr[2] = 0; - for (x=0; xwidth*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 DEBUG - printf("pseudo color match with %d colors per channel\n", cpc); -#endif - for (y=0; yheight; y++) { - for (x=0; xwidth; 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 DEBUG - printf("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)); - - /*#ifdef ASM_X86*/ -#if 0 - x86_PseudoColor_32_to_8(image->data, ximg->image->data, - err+4, nerr+4, - rtable, - dr, dg, db, ctx->pixels, cpc, - image->width, image->height, - channels, - ximg->image->bytes_per_line - image->width); -#else - convertPseudoColor_to_8(ximg, image, err+4, nerr+4, - rtable, gtable, btable, - dr, dg, db, ctx->pixels, cpc); -#endif - - 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; yheight; y++) { - for (x=0; xwidth; 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 DEBUG - printf("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; xwidth*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; yheight; y++) { - if (yheight-1) { - int x1; - for (x=0, x1=(y+1)*image->width*channels; - xwidth*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; xwidth*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<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 DEBUG - printf("grayscale match with %d colors per channel\n", cpc); -#endif - for (y=0; yheight; y++) { - for (x=0; xwidth; 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 DEBUG - printf("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; xwidth; 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; yheight; y++) { - if (yheight-1) { - int x1; - for (x=0, x1=(y+1)*image->width*channels; xwidth; 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; xwidth; 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 XSHM - Pixmap tmp; -#endif - - assert(context!=NULL); - assert(image!=NULL); - assert(pixmap!=NULL); - - switch (context->vclass) { - case TrueColor: -#ifdef BENCH - cycle_bench(1); -#endif - ximg = image2TrueColor(context, image); -#ifdef BENCH - cycle_bench(0); -#endif - break; - - case PseudoColor: - case StaticColor: -#ifdef BENCH - cycle_bench(1); -#endif - if (context->attribs->standard_colormap_mode != RIgnoreStdColormap) - ximg = image2StandardPseudoColor(context, image); - else - ximg = image2PseudoColor(context, image); -#ifdef BENCH - cycle_bench(0); -#endif - 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 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 /* !XSHM */ - RPutXImage(context, *pixmap, context->copy_gc, ximg, 0, 0, 0, 0, - image->width, image->height); -#endif /* !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, 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]<green]<blue]<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<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; -} - - +/* 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., 675 Mass Ave, Cambridge, MA 02139, 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 + +#ifdef BENCH +#include "bench.h" +#endif + +#include "wraster.h" + +#ifdef XSHM +extern Pixmap R_CreateXImageMappedPixmap(RContext * context, RXImage * ximage); + +#endif + +#ifdef ASM_X86 +extern void x86_PseudoColor_32_to_8(unsigned char *image, + unsigned char *ximage, + char *err, char *nerr, + short *ctable, + int dr, int dg, int db, + unsigned long *pixels, + int cpc, int width, int height, int bytesPerPixel, int line_offset); +#endif /* ASM_X86 */ + +#ifdef ASM_X86_MMX + +extern int x86_check_mmx(); + +extern void x86_mmx_TrueColor_32_to_16(unsigned char *image, + unsigned short *ximage, + short *err, short *nerr, + const unsigned short *rtable, + const unsigned short *gtable, + const unsigned short *btable, + int dr, int dg, int db, + unsigned int roffs, + unsigned int goffs, + unsigned int boffs, int width, int height, int line_offset); + +#endif /* ASM_X86_MMX */ + +#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 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; + } + +#ifdef BENCH + cycle_bench(1); +#endif + + 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 DEBUG + puts("true color match"); +#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 DEBUG + puts("true color dither"); +#endif + +#ifdef ASM_X86_MMX + if (ctx->depth == 16 && HAS_ALPHA(image) && x86_check_mmx()) { + short *err; + short *nerr; + + err = malloc(8 * (image->width + 3)); + nerr = malloc(8 * (image->width + 3)); + if (!err || !nerr) { + NFREE(err); + NFREE(nerr); + RErrorCode = RERR_NOMEMORY; + RDestroyXImage(ctx, ximg); + return NULL; + } + memset(err, 0, 8 * (image->width + 3)); + memset(nerr, 0, 8 * (image->width + 3)); + + x86_mmx_TrueColor_32_to_16(image->data, + (unsigned short *)ximg->image->data, + err + 8, nerr + 8, + rtable, gtable, btable, + dr, dg, db, + roffs, goffs, boffs, + image->width, image->height, + ximg->image->bytes_per_line - 2 * image->width); + + free(err); + free(nerr); + } else +#endif /* ASM_X86_MMX */ + { + 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); + } + + } + +#ifdef BENCH + cycle_bench(0); +#endif + + 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 DEBUG + printf("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 DEBUG + printf("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)); + + /*#ifdef ASM_X86 */ +#if 0 + x86_PseudoColor_32_to_8(image->data, ximg->image->data, + err + 4, nerr + 4, + rtable, + dr, dg, db, ctx->pixels, cpc, + image->width, image->height, + channels, ximg->image->bytes_per_line - image->width); +#else + convertPseudoColor_to_8(ximg, image, err + 4, nerr + 4, + rtable, gtable, btable, dr, dg, db, ctx->pixels, cpc); +#endif + + 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 DEBUG + printf("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 DEBUG + printf("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 DEBUG + printf("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 XSHM + Pixmap tmp; +#endif + + assert(context != NULL); + assert(image != NULL); + assert(pixmap != NULL); + + switch (context->vclass) { + case TrueColor: +#ifdef BENCH + cycle_bench(1); +#endif + ximg = image2TrueColor(context, image); +#ifdef BENCH + cycle_bench(0); +#endif + break; + + case PseudoColor: + case StaticColor: +#ifdef BENCH + cycle_bench(1); +#endif + if (context->attribs->standard_colormap_mode != RIgnoreStdColormap) + ximg = image2StandardPseudoColor(context, image); + else + ximg = image2PseudoColor(context, image); +#ifdef BENCH + cycle_bench(0); +#endif + 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 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 /* !XSHM */ + RPutXImage(context, *pixmap, context->copy_gc, ximg, 0, 0, 0, 0, image->width, image->height); +#endif /* !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, 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; +}