/* WMGlobe 1.3  -  All the Earth on a WMaker Icon
 * myconvert.c - an adaptation of wrlib for use in wmglobe
 * initial source taken in WindowMaker-0.20.3/wrlib :
 */

/* convert.c - convert RImage to Pixmap
 *  Raster graphics library
 *
 *  Copyright (c) 1997 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.
 */

#include "wmglobe.h"

typedef struct RConversionTable {
    unsigned short table[256];
    unsigned short index;
    struct RConversionTable *next;
} RConversionTable;


static RConversionTable *conversionTable = NULL;
static RConversionTable *pif[3];
static short *re, *ge, *be;
static short *nre, *nge, *nbe;
static RXImage *ximgok;

void initmyconvert()
{
    pif[0] = (RConversionTable *) malloc(sizeof(RConversionTable));
    pif[1] = (RConversionTable *) malloc(sizeof(RConversionTable));
    pif[2] = (RConversionTable *) malloc(sizeof(RConversionTable));
    re = (short *) malloc((DIAMETRE + 2) * sizeof(short));
    ge = (short *) malloc((DIAMETRE + 2) * sizeof(short));
    be = (short *) malloc((DIAMETRE + 2) * sizeof(short));
    nre = (short *) malloc((DIAMETRE + 2) * sizeof(short));
    nge = (short *) malloc((DIAMETRE + 2) * sizeof(short));
    nbe = (short *) malloc((DIAMETRE + 2) * sizeof(short));
    ximgok = NULL;
    return;
}



static unsigned short *computeTable(unsigned short mask, int hop)
{
    RConversionTable *tmp = conversionTable;
    int i;

    tmp = pif[hop];

    for (i = 0; i < 256; i++)
	tmp->table[i] = (i * mask + 0x7f) / 0xff;

    tmp->index = mask;
    return tmp->table;
}


static RXImage *image2TrueColor(RContext * ctx, RImage * image)
{
    RXImage *ximg;
    register int x, y, r, g, b;
    unsigned char *red, *grn, *blu;
    unsigned long pixel;
    unsigned short rmask, gmask, bmask;
    unsigned short roffs, goffs, boffs;
    unsigned short *rtable, *gtable, *btable;
    int ofs;

    if (ximgok == NULL)
	ximgok =
	    RCreateXImage(ctx, ctx->depth, image->width, image->height);

    ximg = ximgok;
    if (!ximg) {
	puts("err ");
	return NULL;
    }

    red = image->data;
    grn = image->data + 1;
    blu = image->data + 2;

    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, 0);
    gtable = computeTable(gmask, 1);
    btable = computeTable(bmask, 2);

    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
	puts("true color match");
#endif
	for (y = 0, ofs = 0; y < image->height; y++) {
	    for (x = 0; x < image->width; x++, ofs += 3) {
		/* reduce pixel */
		r = rtable[red[ofs]];
		g = gtable[grn[ofs]];
		b = btable[blu[ofs]];
		pixel = (r << roffs) | (g << goffs) | (b << boffs);
		XPutPixel(ximg->image, x, y, pixel);
	    }
	}
    } else {
	/* dither */
	short *rerr, *gerr, *berr;
	short *nrerr, *ngerr, *nberr;
	short *terr;
	int rer, ger, ber;
	const int dr = 0xff / rmask;
	const int dg = 0xff / gmask;
	const int db = 0xff / bmask;
        register int x1;

#ifdef DEBUG
	puts("true color dither");
#endif
	rerr = re;
	gerr = ge;
	berr = be;
	nrerr = nre;
	ngerr = nge;
	nberr = nbe;

	if (!rerr || !gerr || !berr || !nrerr || !ngerr || !nberr) {
	    RErrorCode = RERR_NOMEMORY;
	    RDestroyXImage(ctx, ximg);
	    return NULL;
	}
	for (x = x1 = 0; x < image->width; x++, x1 += 3) {

	    rerr[x] = red[x1];
	    gerr[x] = grn[x1];
	    berr[x] = blu[x1];
	}
	rerr[x] = gerr[x] = berr[x] = 0;
	/* convert and dither the image to XImage */
	for (y = 0, ofs = 0; y < image->height; y++) {
	    if (y < image->height - 1) {

                for (x = 0, x1 = 3 * (ofs + image->width); x <
                     image->width; x++, x1 += 3) {

		    nrerr[x] = red[x1];
		    ngerr[x] = grn[x1];
		    nberr[x] = blu[x1];
		}
		/* last column */
		x1-=3;
		nrerr[x] = red[x1];
		ngerr[x] = grn[x1];
		nberr[x] = blu[x1];
	    }
	    for (x = 0; x < image->width; x++) {
		/* reduce pixel */
		if (rerr[x] > 0xff)
		    rerr[x] = 0xff;
		else if (rerr[x] < 0)
		    rerr[x] = 0;
		if (gerr[x] > 0xff)
		    gerr[x] = 0xff;
		else if (gerr[x] < 0)
		    gerr[x] = 0;
		if (berr[x] > 0xff)
		    berr[x] = 0xff;
		else if (berr[x] < 0)
		    berr[x] = 0;

		r = rtable[rerr[x]];
		g = gtable[gerr[x]];
		b = btable[berr[x]];

		pixel = (r << roffs) | (g << goffs) | (b << boffs);
		XPutPixel(ximg->image, x, y, pixel);
		/* calc error */
		rer = rerr[x] - r * dr;
		ger = gerr[x] - g * dg;
		ber = berr[x] - b * db;

		/* distribute error */
		r = (rer * 3) / 8;
		g = (ger * 3) / 8;
		b = (ber * 3) / 8;
		/* x+1, y */
		rerr[x + 1] += r;
		gerr[x + 1] += g;
		berr[x + 1] += b;
		/* x, y+1 */
		nrerr[x] += r;
		ngerr[x] += g;
		nberr[x] += b;
		/* x+1, y+1 */
		nrerr[x + 1] += rer - 2 * r;
		ngerr[x + 1] += ger - 2 * g;
		nberr[x + 1] += ber - 2 * b;
	    }
	    ofs += image->width;
	    /* skip to next line */
	    terr = rerr;
	    rerr = nrerr;
	    nrerr = terr;

	    terr = gerr;
	    gerr = ngerr;
	    ngerr = terr;

	    terr = berr;
	    berr = nberr;
	    nberr = terr;
	}
    }
    return ximg;
}


static RXImage *image2PseudoColor(RContext * ctx, RImage * image)
{
    RXImage *ximg;
    register int x, y, r, g, b;
    unsigned char *red, *grn, *blu;
    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;
    unsigned char *data;
    int ofs;
    /*register unsigned char maxrgb = 0xff; */

    if (ximgok == NULL)
	ximgok =
	    RCreateXImage(ctx, ctx->depth, image->width, image->height);

    ximg = ximgok;

    if (!ximg) {
	puts("err psc");
	return NULL;
    }
    red = image->data;
    grn = image->data + 1;
    blu = image->data + 2;

    data = ximg->image->data;

    /* Tables are same at the moment because rmask==gmask==bmask. */
    rtable = computeTable(rmask, 0);
    gtable = computeTable(gmask, 1);
    btable = computeTable(bmask, 2);

    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, ofs = 0; y < image->height; y++) {
	    for (x = 0; x < image->width; x++, ofs += 3) {
		/* reduce pixel */
		r = rtable[red[ofs]];
		g = gtable[grn[ofs]];
		b = btable[blu[ofs]];
		pixel = r * cpccpc + g * cpc + b;
		/*data[ofs] = ctx->colors[pixel].pixel; */
		XPutPixel(ximg->image, x, y, ctx->colors[pixel].pixel);
	    }
	}
    } else {
	/* dither */
	short *rerr, *gerr, *berr;
	short *nrerr, *ngerr, *nberr;
	short *terr;
	int rer, ger, ber;
	const int dr = 0xff / rmask;
	const int dg = 0xff / gmask;
	const int db = 0xff / bmask;
        register int x1;

#ifdef DEBUG
	printf("pseudo color dithering with %d colors per channel\n", cpc);
#endif
	rerr = re;
	gerr = ge;
	berr = be;
	nrerr = nre;
	ngerr = nge;
	nberr = nbe;
	if (!rerr || !gerr || !berr || !nrerr || !ngerr || !nberr) {
	    RErrorCode = RERR_NOMEMORY;
	    RDestroyXImage(ctx, ximg);
	    return NULL;
	}
	for (x = x1 = 0; x < image->width; x++, x1+=3) {
	    rerr[x] = red[x1];
	    gerr[x] = grn[x1];
	    berr[x] = blu[x1];
	}
	rerr[x] = gerr[x] = berr[x] = 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 = 3*(ofs + image->width); x <
                     image->width; x++, x1 += 3) {
		    nrerr[x] = red[x1];
		    ngerr[x] = grn[x1];
		    nberr[x] = blu[x1];
		}
		/* last column */
		x1-=3;
		nrerr[x] = red[x1];
		ngerr[x] = grn[x1];
		nberr[x] = blu[x1];
	    }
	    for (x = 0; x < image->width; x++, ofs++) {
		/* reduce pixel */
		if (rerr[x] > 0xff)
		    rerr[x] = 0xff;
		else if (rerr[x] < 0)
		    rerr[x] = 0;
		if (gerr[x] > 0xff)
		    gerr[x] = 0xff;
		else if (gerr[x] < 0)
		    gerr[x] = 0;
		if (berr[x] > 0xff)
		    berr[x] = 0xff;
		else if (berr[x] < 0)
		    berr[x] = 0;

		r = rtable[rerr[x]];
		g = gtable[gerr[x]];
		b = btable[berr[x]];

		pixel = r * cpccpc + g * cpc + b;
		/*data[ofs] = ctx->colors[pixel].pixel; */
		XPutPixel(ximg->image, x, y, ctx->colors[pixel].pixel);

		/* calc error */
		rer = rerr[x] - r * dr;
		ger = gerr[x] - g * dg;
		ber = berr[x] - b * db;

		/* distribute error */
		rerr[x + 1] += (rer * 7) / 16;
		gerr[x + 1] += (ger * 7) / 16;
		berr[x + 1] += (ber * 7) / 16;

		nrerr[x] += (rer * 5) / 16;
		ngerr[x] += (ger * 5) / 16;
		nberr[x] += (ber * 5) / 16;

		if (x > 0) {
		    nrerr[x - 1] += (rer * 3) / 16;
		    ngerr[x - 1] += (ger * 3) / 16;
		    nberr[x - 1] += (ber * 3) / 16;
		}
		nrerr[x + 1] += rer / 16;
		ngerr[x + 1] += ger / 16;
		nberr[x + 1] += ber / 16;
	    }
	    /* skip to next line */
	    terr = rerr;
	    rerr = nrerr;
	    nrerr = terr;

	    terr = gerr;
	    gerr = ngerr;
	    ngerr = terr;

	    terr = berr;
	    berr = nberr;
	    nberr = terr;
	}
    }
    ximg->image->data = (char *) data;

    return ximg;
}


static RXImage *image2GrayScale(RContext * ctx, RImage * image)
{
    RXImage *ximg;
    register int x, y, g;
    unsigned char *red, *grn, *blu;
    const int cpc = ctx->attribs->colors_per_channel;
    unsigned short gmask;
    unsigned short *table;
    unsigned char *data;
    int ofs;
    /*register unsigned char maxrgb = 0xff; */

    if (ximgok == NULL)
	ximgok =
	    RCreateXImage(ctx, ctx->depth, image->width, image->height);

    ximg = ximgok;

    if (!ximg) {
	puts("error!");
	return NULL;
    }
    red = image->data;
    grn = image->data + 1;
    blu = image->data + 2;

    data = ximg->image->data;

    if (ctx->vclass == StaticGray)
	gmask = (1 << ctx->depth) - 1;	/* use all grays */
    else
	gmask = cpc * cpc * cpc - 1;

    table = computeTable(gmask, 0);

    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, ofs = 0; y < image->height; y++) {
	    for (x = 0; x < image->width; x++, ofs++) {
		/* reduce pixel */
		g =
		    table[(red[ofs] * 30 + grn[ofs] * 59 + blu[ofs] * 11) /
			  100];

		/*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 = ge;
	ngerr = nge;
	if (!gerr || !ngerr) {
	    RErrorCode = RERR_NOMEMORY;
	    RDestroyXImage(ctx, ximg);
	    return NULL;
	}
	for (x = 0; x < image->width; x++) {
	    gerr[x] = (red[x] * 30 + grn[x] * 59 + blu[x] * 11) / 100;
	}
	gerr[x] = 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 = ofs + image->width; x < image->width;
		     x++, x1++) {
		    ngerr[x] =
			(red[x1] * 30 + grn[x1] * 59 + blu[x1] * 11) / 100;
		}
		/* last column */
		x1--;
		ngerr[x] =
		    (red[x1] * 30 + grn[x1] * 59 + blu[x1] * 11) / 100;
	    }
	    for (x = 0; x < image->width; x++, ofs++) {
		/* 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;
	}
    }
    ximg->image->data = (char *) data;

    return ximg;
}



int myRConvertImage(RContext * context, RImage * image, Pixmap * pixmap)
{
    RXImage *ximg = NULL;


    assert(context != NULL);
    assert(image != NULL);
    assert(pixmap != NULL);

    /* clear error message */
    if (context->vclass == TrueColor)
	ximg = image2TrueColor(context, image);
    else if (context->vclass == PseudoColor
	     || context->vclass == StaticColor) ximg =
	    image2PseudoColor(context, image);
    else if (context->vclass == GrayScale || context->vclass == StaticGray)
	ximg = image2GrayScale(context, image);

    if (!ximg) {
#ifdef C_ALLOCA
	alloca(0);
#endif
	return False;
    }
/*
 *     *pixmap = XCreatePixmap(context->dpy, context->drawable, image->width,
 *                          image->height, context->depth);
 */


    RPutXImage(context, *pixmap, context->copy_gc, ximg, 0, 0, 0, 0,
	       image->width, image->height);


/*
 *     RDestroyXImage(context, ximg);
 */

#ifdef C_ALLOCA

    alloca(0);
#endif
    return True;
}
