/*
 * This file contains code of three conversion filters based at dithering:
 * truecolor conversion filter - it should convert 8bpp, fixedcolor and
 *    bitmaps to truecolor
 * fixedcolor conversion - emulates user palette at 8bpp displays with static
 *    palette
 * bitmap conversion - emulates 8bpp at 1bpp displays.
 *
 * Since this filters share quite a lot code, they are implemented in one
 * file. Internal loops are quire messy, since they are optimized for speed.
 * Let me know about all ideas to make it faster.
 *
 * Note that quite interesting alg. is for preparing dithering table at
 * fixedcolor displays.
 */
#include <config.h>
#ifndef _plan9_
#ifndef NO_MALLOC_H
#include <malloc.h>
#endif
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#else
#include <u.h>
#include <libc.h>
#include <stdio.h>
#endif
#include <fconfig.h>
#include <filter.h>
#include <xerror.h>
#include <fractal.h>
#include <xthread.h>
#include <archaccel.h>
#define MSIZE 8
static const unsigned char matrix[MSIZE][MSIZE] = {
    {0, 192, 48, 240, 12, 204, 60, 252,},
    {128, 64, 176, 112, 140, 76, 188, 124,},
    {32, 224, 16, 208, 44, 236, 28, 220,},
    {160, 96, 144, 80, 172, 108, 156, 92,},
    {8, 200, 56, 248, 4, 196, 52, 244,},
    {136, 72, 184, 120, 132, 68, 180, 116,},
    {40, 232, 24, 216, 36, 228, 20, 212,},
    {168, 104, 152, 88, 164, 100, 148, 84},
};

struct ditherdata
{
    unsigned char table[32][32][32];
    int rmat[MSIZE][MSIZE];
    int gmat[MSIZE][MSIZE];
    int bmat[MSIZE][MSIZE];
    struct palette *palette;
    int active;
};
struct fixeddata
{
    /*6kKb of table */
    unsigned char ctable[8][8][256];
    /*and 32kB */
    unsigned char table[32][32][32];
    /*and 768 bytes... */
    int rmat[MSIZE][MSIZE];
    int gmat[MSIZE][MSIZE];
    int bmat[MSIZE][MSIZE];
    struct palette *palette;
    int forversion;
    int active;
    int fixcolor;
};
struct bitmapdata
{
    struct palette *palette;
    int intensity[256];
    int forversion;
    int active;
    int fixcolor;
};
static int
requirement (struct filter *f, struct requirements *r)
{
    f->req = *r;
    r->nimages = 1;
    r->flags &= ~IMAGEDATA;
    r->supportedmask = FIXEDCOLOR | MASK1BPP | MASK3BPP | MASK2BPP | MASK4BPP | BITMAPS;
    return (f->next->action->requirement (f->next, r));
}

#ifdef SFIXEDCOLOR
/* create_rgb_table:
 *  Fills an lookup table with conversion data for the specified
 *  palette. 
 *
 *  Uses alg. similar to foodfill - it adds one seed per every color in 
 *  palette to its best position. Then areas around seed are filled by 
 *  same color because it is best aproximation for them, and then areas 
 *  about them etc...
 *
 *  It does just about 80000 tests for distances and this is about 100
 *  times better than normal 256*32000 tests so the caluclation time
 *  is now less than one second at all computers I tested.
 */
#define UNUSED 65535
#define LAST 65532

/* macro add adds to single linked list */
#define add(i)    (next[(i)] == UNUSED ? (next[(i)] = LAST, \
    (first != LAST ? (next[last] = (i)) : (first = (i))), \
    (last = (i))) : 0)

  /* same but w/o checking for first element */
#define add1(i)   (next[(i)] == UNUSED ? (next[(i)] = LAST, \
    next[last] = (i), \
    (last = (i))) : 0)

  /* calculates distance between two colors */
#define dist(a1, a2, a3, b1, b2, b3) \
  (col_diff[0][((a2) - (b2)) & 0x1FF] + \
   col_diff[1][((a1) - (b1)) & 0x1FF] + \
   col_diff[2][((a3) - (b3)) & 0x1FF])

  /* converts r,g,b to position in array and back */
#define pos(r, g, b) \
((((int)(r)) / 8) * 32 * 32 + (((int)(g)) / 8) * 32 + (((int)(b)) / 8))

#define depos(pal, r, g, b) \
  ((b) = ((unsigned int)(pal) & 31) * 8, \
   (g) = (((unsigned int)(pal) >> 5) & 31) * 8, \
   (r) = (((unsigned int)(pal) >> 10) & 31) * 8)

  /* is current color better than pal1? */
#define better(r1, g1, b1, pal1) \
  (((int)dist((r1), (g1), (b1), \
	      (int)(pal1)[0], (int)(pal1)[1], (int)(pal1)[2])) > (int)dist2)
#define better1(r1, g1, b1, pal1) \
  (((int)dist((r1), (g1), (b1), \
	      (int)(pal1)[0], (int)(pal1)[1], (int)(pal1)[2])))

  /* checking of position */
#define dopos(rp, gp, bp, ts) \
  if ((rp > -1 || r > 0) && (rp < 1 || r < 248) && \
      (gp > -1 || g > 0) && (gp < 1 || g < 248) && \
      (bp > -1 || b > 0) && (bp < 1 || b < 248)) { \
    i = first + rp * 32 * 32 + gp * 32 + bp; \
      if (ts ? data[i] != val : !data[i]) { \
	dist2 = (rp ? col_diff[1][(r+8*rp-(int)pal[val][0]) & 0x1FF] : r2) + \
	  (gp ? col_diff[0][(g+8*gp-(int)pal[val][1]) & 0x1FF] : g2) + \
	  (bp ? col_diff[2][(b+8*bp-(int)pal[val][2]) & 0x1FF] : b2); \
	  if (better((r+8*rp), (g+8*gp), (b+8*bp), pal[data[i]])) { \
	    data[i] = val; \
	      add1 (i); \
	  } \
      } \
  }

static void
create_rgb_table (unsigned char table[32][32][32], struct palette *palette)
{
    int i, curr, r, g, b, val, r2, g2, b2, dist2;
    rgb_t *pal = palette->rgb;
    unsigned short *next;
    unsigned char *data;
    int first = LAST;
    int last = LAST;
    int count = 0;

    next = (unsigned short *) malloc (sizeof (short) * 32 * 32 * 32);
    if (col_diff[0][1] == 0)
        bestfit_init ();

    memset_long (next, 255, sizeof (short) * 32 * 32 * 32);
    memset_long (table, palette->start, sizeof (char) * 32 * 32 * 32);
    depos (32 * 32 * 32 - 1, r, g, b);

    data = (unsigned char *) table;

    /* add starting seeds for foodfill */
    for (i = palette->start + 1; i < palette->end; i++) {
        curr = pos ((int) pal[i][0], (int) pal[i][1], (int) pal[i][2]);
        if (next[curr] == UNUSED) {
            data[curr] = (unsigned int) i;
            add (curr);
        }
    }

    /* main foodfill: two versions of loop for faster growing in blue axis */
    while (first != LAST) {
        depos (first, r, g, b);

        /* calculate distance of current color */
        val = data[first];
        r2 = col_diff[1][(((int) pal[val][0]) - (r)) & 0x1FF];
        g2 = col_diff[0][(((int) pal[val][1]) - (g)) & 0x1FF];
        b2 = col_diff[2][(((int) pal[val][2]) - (b)) & 0x1FF];

        /* try to grow to all directions */
        dopos (0, 0, 1, 1);
        dopos (0, 0, -1, 1);
        dopos (1, 0, 0, 1);
        dopos (-1, 0, 0, 1);
        dopos (0, 1, 0, 1);
        dopos (0, -1, 0, 1);

        /* faster growing of blue direction */
        if ((b > 0) && (data[first - 1] == val)) {
            b -= 8;
            first--;
            b2 = col_diff[2][(((int) pal[val][2]) - (b)) & 0x1ff];

            dopos (-1, 0, 0, 0);
            dopos (1, 0, 0, 0);
            dopos (0, -1, 0, 0);
            dopos (0, 1, 0, 0);

            first++;
        }

        /* get next from list */
        i = first;
        first = next[first];
        next[i] = UNUSED;

        /* second version of loop */
        if (first != LAST) {
            depos (first, r, g, b);

            val = data[first];
            r2 = col_diff[1][(((int) pal[val][0]) - (r)) & 0x1ff];
            g2 = col_diff[0][(((int) pal[val][1]) - (g)) & 0x1ff];
            b2 = col_diff[2][(((int) pal[val][2]) - (b)) & 0x1ff];

            dopos (0, 0, 1, 1);
            dopos (0, 0, -1, 1);
            dopos (1, 0, 0, 1);
            dopos (-1, 0, 0, 1);
            dopos (0, 1, 0, 1);
            dopos (0, -1, 0, 1);

            if ((b < 248) && (data[first + 1] == val)) {
                b += 8;
                first++;
                b2 = col_diff[2][(((int) pal[val][2]) - (b)) & 0x1ff];

                dopos (-1, 0, 0, 0);
                dopos (1, 0, 0, 0);
                dopos (0, -1, 0, 0);
                dopos (0, 1, 0, 0);

                first--;
            }

            i = first;
            first = next[first];
            next[i] = UNUSED;
        }

        count++;
    }
    free (next);
}

static void
checksizes (unsigned char table[32][32][32], int *RESTRICT red, int *RESTRICT green, int *RESTRICT blue)
{
    int r, g, b;
    int color;
    int n, maxn;
    maxn = 0;
    n = 0;
    for (r = 0; r < 32; r++)
        for (g = 0; g < 32; g++) {
            color = 512;
            for (b = 0; b < 32; b++)
                if (color != table[r][g][b]) {
                    if (maxn < n)
                        maxn = n;
                    color = table[r][g][b];
                    n = 0;
                } else
                    n++;
        }
    *blue = (maxn) * 8;
    maxn = 0;
    n = 0;
    for (b = 0; b < 32; b++)
        for (g = 0; g < 32; g++) {
            color = 512;
            for (r = 0; r < 32; r++)
                if (color != table[r][g][b]) {
                    if (maxn < n)
                        maxn = n;
                    color = table[r][g][b];
                    n = 0;
                } else
                    n++;
        }
    *red = (maxn) * 8;
    maxn = 0;
    n = 0;
    for (b = 0; b < 32; b++)
        for (r = 0; r < 32; r++) {
            color = 512;
            for (g = 0; g < 32; g++)
                if (color != table[r][g][b]) {
                    if (maxn < n)
                        maxn = n;
                    color = table[r][g][b];
                    n = 0;
                } else
                    n++;
        }
    *green = (maxn) * 8;
}
#endif /*FIXEDCOLOR */
static int
initialize (struct filter *f, struct initdata *i)
{
    struct ditherdata *s = (struct ditherdata *) f->data;
    struct palette *palette;
    int r, g, b;
    inhermisc (f, i);
    if (i->image->bytesperpixel <= 1) {
        int red, green, blue;
        if (!inherimage (f, i, TOUCHIMAGE /*| IMAGEDATA */ , 0, 0, s->palette, 0, 0))
            return 0;
        if (!s->active) {
            palette = clonepalette (f->image->palette);
            restorepalette (s->palette, palette);
            destroypalette (palette);
        }
        switch (f->image->palette->type) {
            case C256:
                blue = mkrgb (f->image->palette);
                red = blue / 256 / 256;
                green = (blue / 256) & 255;
                blue &= 255;
                for (r = 0; r < 32; r++)
                    for (g = 0; g < 32; g++)
                        for (b = 0; b < 32; b++) {
                            s->table[r][g][b] = f->image->palette->pixels[((r * red + red / 2) / 32) + ((g * green + green / 2) / 32) * blue * red + ((b * blue + blue / 2) / 32) * red];
                        }
                for (r = 0; r < MSIZE; r++)
                    for (g = 0; g < MSIZE; g++) {
                        s->rmat[r][g] = ((int) matrix[r][g] - 128) * 256 / red / 256;
                        s->gmat[r][g] = ((int) matrix[(r + 3) % MSIZE][(g + 6) % MSIZE] - 128) * 256 / green / 256;
                        s->bmat[r][g] = ((int) matrix[(r + 6) % MSIZE][(g + 3) % MSIZE] - 128) * 256 / blue / 256;
                    }
                break;
#ifdef SFIXEDCOLOR
            case FIXEDCOLOR:
                create_rgb_table (s->table, f->image->palette);
                checksizes (s->table, &red, &green, &blue);
                for (r = 0; r < MSIZE; r++)
                    for (g = 0; g < MSIZE; g++) {
                        s->rmat[r][g] = ((int) matrix[r][g] - 128) * red / 256;
                        s->gmat[r][g] = ((int) matrix[(r + 3) % MSIZE][(g + 6) % MSIZE] - 128) * green / 256;
                        s->bmat[r][g] = ((int) matrix[(r + 6) % MSIZE][(g + 3) % MSIZE] - 128) * blue / 256;
                    }
                break;
#endif
#ifdef BITMAPS
            case MBITMAP:
            case LBITMAP:
            case MIBITMAP:
            case LIBITMAP:
                break;
#endif
            case GRAYSCALE:
                break;
            default:
                x_fatalerror ("Unsupported image type. Recompile XaoS.");
        }
        setfractalpalette (f, s->palette);
        s->active = 1;
        f->queue->saveimage = f->childimage;
        return (f->previous->action->initialize (f->previous, i));
    } else {
        if (s->active) {
            f->image = i->image;
            palette = clonepalette (s->palette);
            restorepalette (f->image->palette, palette);
            destroypalette (palette);
        }
        s->active = 0;
        return (f->previous->action->initialize (f->previous, i));
    }
}

static struct filter *
getinstance (const struct filteraction *a)
{
    struct filter *f = createfilter (a);
    struct ditherdata *i = (struct ditherdata *) calloc (1, sizeof (*i));
    i->palette = createpalette (0, 65536, TRUECOLOR, 0, 65536, NULL, NULL, NULL, NULL, NULL);
    f->data = i;
    f->name = "Truecolor to 8bpp converter";
    return (f);
}

static void
convert (void *data, struct taskinfo *task, int r1, int r2)
{
    struct filter *RESTRICT f = (struct filter *) data;
    struct image *RESTRICT img1 = f->childimage;
    struct image *RESTRICT img2 = f->image;
    const struct ditherdata *RESTRICT s = (struct ditherdata *) f->data;
    const pixel32_t *RESTRICT src, *srcend;
    int r, g, b;
    pixel8_t *RESTRICT dest;
    int i;
    int x = 0;

    for (i = r1; i < r2; i++) {
        src = (pixel32_t *) img1->currlines[i];
        dest = img2->currlines[i];
        srcend = src + img1->width;
        x++;
        x = x & (MSIZE - 1);
        for (; src < srcend; src++, dest++) {
            b = *src;
            g = (b >> 8) & 0xff;
            r = (b >> 16);
            b &= 0xff;
            r += s->rmat[x][(unsigned long) dest & (MSIZE - 1)];
            g += s->gmat[x][(unsigned long) dest & (MSIZE - 1)];
            b += s->bmat[x][(unsigned long) dest & (MSIZE - 1)];
            if (r & (~255)) {
                if (r < 0)
                    r = 0;
                else if (r > 255)
                    r = 255;
            }
            if (g & (~255)) {
                if (g < 0)
                    g = 0;
                else if (g > 255)
                    g = 255;
            }
            if (b & (~255)) {
                if (b < 0)
                    b = 0;
                else if (b > 255)
                    b = 255;
            }
            *dest = s->table[r >> 3][g >> 3][b >> 3];
        }
    }
}

#define intenzity(x) ((int)(((x)&255) * 76 + (((x)>>8)&255) * 151 + (((x)>>16)&255) * 28)>>8)
static void
convertgray (void *data, struct taskinfo *task, int r1, int r2)
{
    struct filter *f = (struct filter *) data;
    struct image *img1 = f->childimage;
    struct image *img2 = f->image;
    pixel32_t *src, *srcend;
    pixel8_t *dest;
    int i;
    int x = 0;
    unsigned char table[256];
    for (i = 0; i < 256; i++)
        table[i] = i * (img2->palette->end - img2->palette->start) / 256 + img2->palette->start;

    for (i = r1; i < r2; i++) {
        src = (pixel32_t *) img1->currlines[i];
        dest = img2->currlines[i];
        srcend = src + img1->width;
        x++;
        x = x & (MSIZE - 1);
        for (; src < srcend; src++, dest++) {
            *dest = table[intenzity (*src)];
        }
    }
}

#ifdef SBITMAPS
#define inten(x) ((int)(((x)&255) * 76 + (((x)>>8)&255) * 151 + (((x)>>16)&255) * 28)>>8)-256
static void
converttbitmap (void *data, struct taskinfo *task, int r1, int r2)
{
    struct filter *f = (struct filter *) data;
    struct image *img1 = f->childimage;
    struct image *img2 = f->image;
    pixel32_t *src, *srcend;
    pixel8_t *dest = NULL;
    int i, y;
    unsigned int mask = 0;
    int x = 0;
    unsigned int val = 0;
    src = (pixel32_t *) img1->currlines[0];
    for (i = r1; i < r2; i++) {
        dest = img2->currlines[i];
        x = i & (MSIZE - 1);
        src = (pixel32_t *) img1->currlines[i];
        srcend = src + (img1->width & ~7);
        switch (img2->palette->type) {
#ifdef SMBITMAPS
            case MBITMAP:
                for (; src < srcend; dest++) {
                    if (inten (src[0]) + (int) matrix[x][0] >= 0)
                        val = 128;
                    else
                        val = 0;
                    if (inten (src[1]) + (int) matrix[x][1] >= 0)
                        val |= 64;
                    if (inten (src[2]) + (int) matrix[x][2] >= 0)
                        val |= 32;
                    if (inten (src[3]) + (int) matrix[x][3] >= 0)
                        val |= 16;
                    if (inten (src[4]) + (int) matrix[x][4] >= 0)
                        val |= 8;
                    if (inten (src[5]) + (int) matrix[x][5] >= 0)
                        val |= 4;
                    if (inten (src[6]) + (int) matrix[x][6] >= 0)
                        val |= 2;
                    if (inten (src[7]) + (int) matrix[x][7] >= 0)
                        val |= 1;
                    src += 8;
                    *dest = val;
                }
                srcend = (pixel32_t *) img1->currlines[i] + img1->width;
                if (src != srcend) {
                    y = 0;
                    for (val = 0, mask = 128; src < srcend; mask >>= 1, src++, y++) {
                        if (inten (*src) + (int) matrix[x][y] >= 0)
                            val |= mask;
                    }
                    if (!mask)
                        *dest = val, dest++;
                }
                break;
            case MIBITMAP:
                for (; src < srcend; dest++) {
                    if (inten (src[0]) + (int) matrix[x][0] <= 0)
                        val = 128;
                    else
                        val = 0;
                    if (inten (src[1]) + (int) matrix[x][1] <= 0)
                        val |= 64;
                    if (inten (src[2]) + (int) matrix[x][2] <= 0)
                        val |= 32;
                    if (inten (src[3]) + (int) matrix[x][3] <= 0)
                        val |= 16;
                    if (inten (src[4]) + (int) matrix[x][4] <= 0)
                        val |= 8;
                    if (inten (src[5]) + (int) matrix[x][5] <= 0)
                        val |= 4;
                    if (inten (src[6]) + (int) matrix[x][6] <= 0)
                        val |= 2;
                    if (inten (src[7]) + (int) matrix[x][7] <= 0)
                        val |= 1;
                    src += 8;
                    *dest = val;
                }
                srcend = (pixel32_t *) img1->currlines[i] + img1->width;
                if (src != srcend) {
                    y = 0;
                    for (val = 0, mask = 128; src < srcend; mask >>= 1, src++, y++) {
                        if (inten (*src) + (int) matrix[x][y] <= 0)
                            val |= mask;
                    }
                    if (!mask)
                        *dest = val, dest++;
                }
                break;
#endif
#ifdef SLBITMAPS
            case LBITMAP:
                for (; src < srcend; dest++) {
                    if (inten (src[0]) + (int) matrix[x][0] >= 0)
                        val = 1;
                    else
                        val = 0;
                    if (inten (src[1]) + (int) matrix[x][1] >= 0)
                        val |= 2;
                    if (inten (src[2]) + (int) matrix[x][2] >= 0)
                        val |= 4;
                    if (inten (src[3]) + (int) matrix[x][3] >= 0)
                        val |= 8;
                    if (inten (src[4]) + (int) matrix[x][4] >= 0)
                        val |= 16;
                    if (inten (src[5]) + (int) matrix[x][5] >= 0)
                        val |= 32;
                    if (inten (src[6]) + (int) matrix[x][6] >= 0)
                        val |= 64;
                    if (inten (src[7]) + (int) matrix[x][7] >= 0)
                        val |= 128;
                    src += 8;
                    *dest = val;
                }
                srcend = (pixel32_t *) img1->currlines[i] + img1->width;
                if (src != srcend) {
                    y = 0;
                    for (val = 0, mask = 1; src < srcend; mask <<= 1, src++, y++) {
                        if (inten (*src) + (int) matrix[x][y] >= 0)
                            val |= mask;
                    }
                    if (!mask)
                        *dest = val, dest++;
                }
                break;
            case LIBITMAP:
                for (; src < srcend; dest++) {
                    if (inten (src[0]) + (int) matrix[x][0] <= 0)
                        val = 1;
                    else
                        val = 0;
                    if (inten (src[1]) + (int) matrix[x][1] <= 0)
                        val |= 2;
                    if (inten (src[2]) + (int) matrix[x][2] <= 0)
                        val |= 4;
                    if (inten (src[3]) + (int) matrix[x][3] <= 0)
                        val |= 8;
                    if (inten (src[4]) + (int) matrix[x][4] <= 0)
                        val |= 16;
                    if (inten (src[5]) + (int) matrix[x][5] <= 0)
                        val |= 32;
                    if (inten (src[6]) + (int) matrix[x][6] <= 0)
                        val |= 64;
                    if (inten (src[7]) + (int) matrix[x][7] <= 0)
                        val |= 128;
                    src += 8;
                    *dest = val;
                }
                srcend = (pixel32_t *) img1->currlines[i] + img1->width;
                if (src != srcend) {
                    y = 0;
                    for (val = 0, mask = 1; src < srcend; mask <<= 1, src++, y++) {
                        if (inten (*src) + (int) matrix[x][y] <= 0)
                            val |= mask;
                    }
                    if (!mask)
                        *dest = val, dest++;
                }
                break;
#endif
        }
    }
    *dest = val;
}
#endif
static void
destroyinstance (struct filter *f)
{
    struct ditherdata *i = (struct ditherdata *) f->data;
    destroypalette (i->palette);
    free (f->data);
    destroyinheredimage (f);
    free (f);
}

static int
doit (struct filter *f, int flags, int time)
{
    int val;
    struct ditherdata *s = (struct ditherdata *) f->data;
    if (s->active)
        updateinheredimage (f);
    val = f->previous->action->doit (f->previous, flags, time);
    if (s->active) {
#ifdef SBITMAPS
        if (f->image->palette->type & BITMAPS) {
            xth_function (converttbitmap, f, f->image->height);
        } else
#endif
        {
            if (f->image->palette->type == GRAYSCALE)
                xth_function (convertgray, f, f->image->height);
            else
                xth_function (convert, f, f->image->height);
        }
        xth_sync ();
    }
    return val;
}

static void
myremovefilter (struct filter *f)
{
    struct ditherdata *s = (struct ditherdata *) f->data;
    struct palette *palette;
    if (s->active) {
        palette = clonepalette (s->palette);
        restorepalette (f->image->palette, palette);
        destroypalette (palette);
    }
}


const struct filteraction truecolor_filter = {
    "Truecolor emulator",
    "truecolor",
    0,
    getinstance,
    destroyinstance,
    doit,
    requirement,
    initialize,
    convertupgeneric,
    convertdowngeneric,
    myremovefilter,
};



#ifdef SCONVERTORS
static int
myfixedalloccolor (struct palette *p, int init, int r, int g, int b)
{
    struct palette *palette = (struct palette *) p->data;
    (void) fixedalloccolor (p, init, r, g, b);
    return (palette->alloccolor (palette, init, r, g, b));
}

static void
myallocfinished (struct palette *p)
{
    struct palette *palette = (struct palette *) p->data;
    palette->allocfinished (palette);
}

static void
mysetcolor (struct palette *p, int start, int end, rgb_t * rgb)
{
    p->data = &p;
}
#endif
#ifdef SFIXEDCOLOR

static int
initializefixed (struct filter *f, struct initdata *i)
{
    struct fixeddata *s = (struct fixeddata *) f->data;
    struct palette *palette;
    int r, g;
    inhermisc (f, i);
    if (i->image->palette->type == FIXEDCOLOR && !(f->req.supportedmask & FIXEDCOLOR)) {
        int red, green, blue;
        i->image->palette->alloccolor = myfixedalloccolor;
        i->image->palette->allocfinished = myallocfinished;
        i->image->palette->data = s->palette;
        if (!inherimage (f, i, TOUCHIMAGE | IMAGEDATA, 0, 0, s->palette, 0, 0))
            return 0;
        if (s->active == -1) {
            palette = clonepalette (f->image->palette);
            restorepalette (s->palette, palette);
            destroypalette (palette);
        }
        create_rgb_table (s->table, f->image->palette);
        checksizes (s->table, &red, &green, &blue);
        for (r = 0; r < MSIZE; r++)
            for (g = 0; g < MSIZE; g++) {
                s->rmat[r][g] = ((int) matrix[r][g] - 128) * red / 256;
                s->gmat[r][g] = ((int) matrix[(r + 3) % MSIZE][(g + 6) % MSIZE] - 128) * green / 256;
                s->bmat[r][g] = ((int) matrix[(r + 6) % MSIZE][(g + 3) % MSIZE] - 128) * blue / 256;
            }
        s->palette->data = &s->palette;
        setfractalpalette (f, s->palette);
        s->active = 1;
        f->queue->saveimage = f->childimage;
        f->queue->palettechg = f;
        return (f->previous->action->initialize (f->previous, i));
    } else {
        if (s->active == 1) {
            s->fixcolor = 1;
        }
        s->active = 0;
        return (f->previous->action->initialize (f->previous, i));
    }
}

static struct filter *
getinstancefixed (const struct filteraction *a)
{
    struct filter *f = createfilter (a);
    struct fixeddata *i = (struct fixeddata *) calloc (1, sizeof (*i));
    i->palette = createpalette (0, 256, C256, 0, 256, NULL, mysetcolor, NULL, NULL, NULL);
    i->active = -1;
    f->data = i;
    f->name = "Palete emulator";
    return (f);
}

static void
convertfixed (void *data, struct taskinfo *task, int r1, int r2)
{
    struct filter *f = (struct filter *) data;
    struct image *img1 = f->childimage;
    struct image *img2 = f->image;
    struct fixeddata *s = (struct fixeddata *) f->data;
    pixel8_t *src, *srcend;
    pixel8_t *dest;
    int i;
    int x = 0;
    for (i = r1; i < r2; i++) {
        src = (pixel8_t *) img1->currlines[i];
        dest = img2->currlines[i];
        srcend = src + img1->width;
        x++;
        x = x & (MSIZE - 1);
        for (; src < srcend; src++, dest++) {
            *dest = s->ctable[x][((unsigned long) dest) & (MSIZE - 1)][*src];
        }
    }
}

static int
doitfixed (struct filter *f, int flags, int time)
{
    int val;
    struct fixeddata *s = (struct fixeddata *) f->data;
    if (s->fixcolor && !s->active) {
        struct palette *palette;
        s->fixcolor = 0;
        palette = clonepalette (s->palette);
        restorepalette (f->previous->childimage->palette, palette);
        destroypalette (palette);
    }
    if (s->active)
        updateinheredimage (f);
    if (!(flags & PALETTEONLY))
        val = f->previous->action->doit (f->previous, flags, time);
    else
        val = 0;
    if (s->active) {
        if (s->palette->data != NULL) {
            int i, x, y;
            s->palette->data = NULL;
            val |= CHANGED;
            for (i = 0; i < 256; i++) {
                for (x = 0; x < 8; x++)
                    for (y = 0; y < 8; y++) {
                        int r, g, b;
                        r = s->palette->rgb[i][0] + s->rmat[x][y];
                        if (r & (~255)) {
                            if (r < 0)
                                r = 0;
                            else if (r > 255)
                                r = 255;
                        }
                        g = s->palette->rgb[i][1] + s->gmat[x][y];
                        if (g & (~255)) {
                            if (g < 0)
                                g = 0;
                            else if (g > 255)
                                g = 255;
                        }
                        b = s->palette->rgb[i][2] + s->bmat[x][y];
                        if (b & (~255)) {
                            if (b < 0)
                                b = 0;
                            else if (b > 255)
                                b = 255;
                        }
                        s->ctable[x][y][i] = s->table[r >> 3][g >> 3][b >> 3];
                    }
            }
        }
        xth_function (convertfixed, f, f->image->height);
        xth_sync ();
    }
    return val;
}

static void
myremovefilterfixed (struct filter *f)
{
    struct fixeddata *s = (struct fixeddata *) f->data;
    struct palette *palette;
    if (s->active) {
        palette = clonepalette (s->palette);
        restorepalette (f->image->palette, palette);
        destroypalette (palette);
    }
}

static void
destroyinstancefixed (struct filter *f)
{
    struct fixeddata *i = (struct fixeddata *) f->data;
    destroypalette (i->palette);
    free (f->data);
    destroyinheredimage (f);
    free (f);
}

const struct filteraction fixedcolor_filter = {
    "Palette emulator",
    "fixedcolor",
    0,
    getinstancefixed,
    destroyinstancefixed,
    doitfixed,
    requirement,
    initializefixed,
    convertupgeneric,
    convertdowngeneric,
    myremovefilterfixed,
};
#endif

#ifdef SBITMAPS
static int
initializebitmap (struct filter *f, struct initdata *i)
{
    struct bitmapdata *s = (struct bitmapdata *) f->data;
    struct palette *palette;
    inhermisc (f, i);
    if ((i->image->palette->type & BITMAPS) && !(f->req.supportedmask & BITMAPS)) {
        i->image->palette->alloccolor = myfixedalloccolor;
        i->image->palette->allocfinished = myallocfinished;
        i->image->palette->data = s->palette;
        if (!inherimage (f, i, TOUCHIMAGE | IMAGEDATA, 0, 0, s->palette, 0, 0))
            return 0;
        if (s->active == -1) {
            palette = clonepalette (f->image->palette);
            restorepalette (s->palette, palette);
            destroypalette (palette);
        }
        s->palette->data = &s->palette;
        setfractalpalette (f, s->palette);
        s->active = 1;
        f->queue->saveimage = f->childimage;
        f->queue->palettechg = f;
        return (f->previous->action->initialize (f->previous, i));
    } else {
        if (s->active == 1) {
            s->fixcolor = 1;
        }
        s->active = 0;
        return (f->previous->action->initialize (f->previous, i));
    }
}

static struct filter *
getinstancebitmap (const struct filteraction *a)
{
    struct filter *f = createfilter (a);
    struct bitmapdata *i = (struct bitmapdata *) calloc (1, sizeof (*i));
    i->palette = createpalette (0, 256, C256, 0, 256, NULL, mysetcolor, NULL, NULL, NULL);
    i->active = -1;
    f->data = i;
    f->name = "Palete emulator";
    return (f);
}

#define INTENSITY(r,g,b) (r * 30U + g * 59U + b * 11U)/100U-256U
static void
convertbitmap (void *data, struct taskinfo *task, int r1, int r2)
{
    struct filter *f = (struct filter *) data;
    struct image *img1 = f->childimage;
    struct image *img2 = f->image;
    struct bitmapdata *s = (struct bitmapdata *) f->data;
    pixel8_t *src, *srcend;
    pixel8_t *dest = NULL;
    int *intensity = s->intensity;
    int i;
    unsigned int mask = 0;
    int x = 0, y;
    unsigned int val = 0;
    src = (pixel8_t *) img1->currlines[0];
    for (i = r1; i < r2; i++) {
        dest = img2->currlines[i];
        x = i & (MSIZE - 1);
        src = (pixel8_t *) img1->currlines[i];
        srcend = src + (img1->width & ~7);
        switch (img2->palette->type) {
#ifdef SMBITMAPS
            case MBITMAP:
                for (; src < srcend; dest++) {
                    if (intensity[src[0]] + (int) matrix[x][0] >= 0)
                        val = 128;
                    else
                        val = 0;
                    if (intensity[src[1]] + (int) matrix[x][1] >= 0)
                        val |= 64;
                    if (intensity[src[2]] + (int) matrix[x][2] >= 0)
                        val |= 32;
                    if (intensity[src[3]] + (int) matrix[x][3] >= 0)
                        val |= 16;
                    if (intensity[src[4]] + (int) matrix[x][4] >= 0)
                        val |= 8;
                    if (intensity[src[5]] + (int) matrix[x][5] >= 0)
                        val |= 4;
                    if (intensity[src[6]] + (int) matrix[x][6] >= 0)
                        val |= 2;
                    if (intensity[src[7]] + (int) matrix[x][7] >= 0)
                        val |= 1;
                    src += 8;
                    *dest = val;
                }
                srcend = (pixel8_t *) img1->currlines[i] + img1->width;
                if (src != srcend) {
                    y = 0;
                    for (val = 0, mask = 128; src < srcend; mask >>= 1, src++, y++) {
                        if (intensity[*src] + (int) matrix[x][y] >= 0)
                            val |= mask;
                    }
                    *dest = val, dest++;
                }
                break;
            case MIBITMAP:
                for (; src < srcend; dest++) {
                    if (intensity[src[0]] + (int) matrix[x][0] <= 0)
                        val = 128;
                    else
                        val = 0;
                    if (intensity[src[1]] + (int) matrix[x][1] <= 0)
                        val |= 64;
                    if (intensity[src[2]] + (int) matrix[x][2] <= 0)
                        val |= 32;
                    if (intensity[src[3]] + (int) matrix[x][3] <= 0)
                        val |= 16;
                    if (intensity[src[4]] + (int) matrix[x][4] <= 0)
                        val |= 8;
                    if (intensity[src[5]] + (int) matrix[x][5] <= 0)
                        val |= 4;
                    if (intensity[src[6]] + (int) matrix[x][6] <= 0)
                        val |= 2;
                    if (intensity[src[7]] + (int) matrix[x][7] <= 0)
                        val |= 1;
                    src += 8;
                    *dest = val;
                }
                srcend = (pixel8_t *) img1->currlines[i] + img1->width;
                if (src != srcend) {
                    y = 0;
                    for (val = 0, mask = 128; src < srcend; mask >>= 1, src++, y++) {
                        if (intensity[*src] + (int) matrix[x][y] <= 0)
                            val |= mask;
                    }
                    *dest = val, dest++;
                }
                break;
#endif
#ifdef SLBITMAPS
            case LBITMAP:
                for (; src < srcend; dest++) {
                    if (intensity[src[0]] + (int) matrix[x][0] >= 0)
                        val = 1;
                    else
                        val = 0;
                    if (intensity[src[1]] + (int) matrix[x][1] >= 0)
                        val |= 2;
                    if (intensity[src[2]] + (int) matrix[x][2] >= 0)
                        val |= 4;
                    if (intensity[src[3]] + (int) matrix[x][3] >= 0)
                        val |= 8;
                    if (intensity[src[4]] + (int) matrix[x][4] >= 0)
                        val |= 16;
                    if (intensity[src[5]] + (int) matrix[x][5] >= 0)
                        val |= 32;
                    if (intensity[src[6]] + (int) matrix[x][6] >= 0)
                        val |= 64;
                    if (intensity[src[7]] + (int) matrix[x][7] >= 0)
                        val |= 128;
                    src += 8;
                    *dest = val;
                }
                srcend = (pixel8_t *) img1->currlines[i] + img1->width;
                if (src != srcend) {
                    y = 0;
                    for (val = 0, mask = 1; src < srcend; mask <<= 1, src++, y++) {
                        if (intensity[*src] + (int) matrix[x][y] >= 0)
                            val |= mask;
                    }
                    *dest = val, dest++;
                }
            case LIBITMAP:
                for (; src < srcend; dest++) {
                    if (intensity[src[0]] + (int) matrix[x][0] <= 0)
                        val = 1;
                    else
                        val = 0;
                    if (intensity[src[1]] + (int) matrix[x][1] <= 0)
                        val |= 2;
                    if (intensity[src[2]] + (int) matrix[x][2] <= 0)
                        val |= 4;
                    if (intensity[src[3]] + (int) matrix[x][3] <= 0)
                        val |= 8;
                    if (intensity[src[4]] + (int) matrix[x][4] <= 0)
                        val |= 16;
                    if (intensity[src[5]] + (int) matrix[x][5] <= 0)
                        val |= 32;
                    if (intensity[src[6]] + (int) matrix[x][6] <= 0)
                        val |= 64;
                    if (intensity[src[7]] + (int) matrix[x][7] <= 0)
                        val |= 128;
                    src += 8;
                    *dest = val;
                }
                srcend = (pixel8_t *) img1->currlines[i] + img1->width;
                if (src != srcend) {
                    y = 0;
                    for (val = 0, mask = 1; src < srcend; mask <<= 1, src++, y++) {
                        if (intensity[*src] + (int) matrix[x][y] <= 0)
                            val |= mask;
                    }
                    *dest = val, dest++;
                }
                break;
#endif
        }
    }
}

static int
doitbitmap (struct filter *f, int flags, int time)
{
    int val;
    struct bitmapdata *s = (struct bitmapdata *) f->data;
    if (s->fixcolor && !s->active) {
        struct palette *palette;
        s->fixcolor = 0;
        palette = clonepalette (s->palette);
        restorepalette (f->previous->childimage->palette, palette);
        destroypalette (palette);
    }
    if (s->active)
        updateinheredimage (f);
    if (!(flags & PALETTEONLY))
        val = f->previous->action->doit (f->previous, flags, time);
    else
        val = 0;
    if (s->active) {
        if (s->palette->data != NULL) {
            int i;
            for (i = 0; i < 256; i++)
                s->intensity[i] = INTENSITY (f->childimage->palette->rgb[i][0], f->childimage->palette->rgb[i][1], f->childimage->palette->rgb[i][2]);
            val |= CHANGED;
        }
        xth_function (convertbitmap, f, f->image->height);
        xth_sync ();
    }
    return val;
}

static void
myremovefilterbitmap (struct filter *f)
{
    struct bitmapdata *s = (struct bitmapdata *) f->data;
    struct palette *palette;
    if (s->active) {
        palette = clonepalette (s->palette);
        restorepalette (f->image->palette, palette);
        destroypalette (palette);
    }
}

static void
destroyinstancebitmap (struct filter *f)
{
    struct bitmapdata *i = (struct bitmapdata *) f->data;
    destroypalette (i->palette);
    free (f->data);
    destroyinheredimage (f);
    free (f);
}

const struct filteraction bitmap_filter = {
    "Palette emulator",
    "bitmap",
    0,
    getinstancebitmap,
    destroyinstancebitmap,
    doitbitmap,
    requirement,
    initializebitmap,
    convertupgeneric,
    convertdowngeneric,
    myremovefilterbitmap,
};
#endif
