/* 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 <config.h>


#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include <assert.h>


#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,
                                       short *rtable, short *gtable,
                                       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 short *rtable,
                         const short *gtable,
                         const 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 */
        {
            char *err;
            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 short *rtable,
                        const short *gtable,
                        const 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 = 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 */
        char *err;
        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;
}




syntax highlighted by Code2HTML, v. 0.9.1