#ifndef IMAGE_H
#define IMAGE_H
#include "config.h"

typedef unsigned char pixel_t;
typedef unsigned char
    rgb_t[4]; /*4 is better than 3 - makes multiplying easier */
struct truec {
    int rshift, gshift, bshift; /*the shift ammounts */
    int rprec, gprec, bprec;    /*precisity - 0=8bit, 1=7bit, -1=9bit etc... */
    unsigned int rmask, gmask, bmask; /*masks */
    unsigned int mask1, mask2,
        allmask;     /*Mask1 and mask2 are distinc color masks
                        allmask are mask for all colors */
    int byteexact;   /*When every colors is at one byte */
    int missingbyte; /*for 32bit truecolor and exact byte places one byte is
                        unused... */
    int alpha;
};
union paletteinfo {
    struct truec truec;
};
struct palette {
    int start;
    int end;
    int maxentries;
    int version;
    int type;
    unsigned int *pixels;
    int npreallocated;
    rgb_t *rgb;
    int flags;
    int (*alloccolor)(struct palette *pal, int init, int r, int g, int b);
    void (*setpalette)(struct palette *pal, int start, int end, rgb_t *rgb);
    void (*allocfinished)(struct palette *pal);
    void (*cyclecolors)(struct palette *pal, int direction);
    int size;   /*number of allocated color entries */
    void *data; /*userdata */
    /*Preallocated palette cells */
    int ncells;
    unsigned int *index;
    const rgb_t *prergb;
    union paletteinfo info;
};

struct image {
    float pixelwidth, pixelheight;
    pixel_t **oldlines;
    pixel_t **currlines;
    void (*flip)(struct image *img);
    int width, height, nimages;
    int bytesperpixel;
    int currimage;
    int flags;
    int scanline;
    int version;
    struct palette *palette;
    void *data; /*userdata */
    void (*free)(struct image *img);
};

#define interpol1(i1, i2, n, mask)                                             \
    ((((i1) & (mask)) * (n) + ((i2) & (mask)) * (256 - (n))) & ((mask) << 8))
#define interpol(i1, i2, n, mr, mg, mb)                                        \
    ((interpol1(i1, i2, n, mr) + interpol1(i1, i2, n, mg) +                    \
      interpol1(i1, i2, n, mb)) >>                                             \
     8)
#define intergray(i1, i2, n) (((i1)*n + (i2) * (256 - (n))) >> 8)
/*
 * J.B. Langston 3/13/2008
 *
 * The Mac OS X driver requires a 32-bit rgb mask where the most significant
 * byte is on (e.g., 0xffffff00). This exposed a bug in the interpol macro
 * that resulted in distorted colors for the smooth coloring modes.
 * If the interpol macro is applied to such a mask, it causes an overflow
 * of the 32-bit int, and the left-most color byte is lost.
 *
 * I added shiftinterpol macro to handle such masks. It shifts everything 1
 * byte to the right, performs the calculation, and then shifts everything
 * back 1 byte to the left when it is done.
 *
 * I also created the safeinterpol macro which detects if the most
 * significant byte in the mask is on, and uses the shiftinterpol macro if
 * so, or the original interpol macro if not.
 *
 * I then modified the interpoltype macro to use the safeinterpol macro
 * instead of the interpol macro directly.
 *
 * J.B. Langston 1/7/2020
 *
 * And another bug on Mac. Now macOS won't display the image properly unless
 * the alpha channel is set to 0xFF, so we force it to 0xFF here using
 * ~(palette).info.truec.allmask, which gives the bit not used by RGB.
 */
#define shiftinterpol(i1, i2, n, mr, mg, mb)                                   \
    (interpol((i1) >> 8, (i2) >> 8, n, (mr) >> 8, (mg) >> 8, (mb) >> 8) << 8)
#define safeinterpol(i1, i2, n, mr, mg, mb)                                    \
    ((((mr) | (mg) | (mb)) & 0xff000000)                                       \
         ? shiftinterpol(i1, i2, n, mr, mg, mb)                                \
         : interpol(i1, i2, n, mr, mg, mb))
#define interpoltype(palette, i1, i2, n)                                       \
    ((palette).type == GRAYSCALE || (palette).type == LARGEITER                \
         ? intergray(i1, i2, n)                                                \
         : safeinterpol(i1, i2, n, (palette).info.truec.rmask,                 \
                        (palette).info.truec.gmask,                            \
                        (palette).info.truec.bmask) |                          \
               ~(palette).info.truec.allmask)
/*palette flags */
#define UNKNOWNENTRIES 1
#define DONOTCHANGE 2
#define FINISHLATER 4
#define UNFINISHED 8
/*image flags */
#define FREELINES 1
#define FREEDATA 2
#define AAIMAGE 4
#define PROTECTBUFFERS 8
/*palette types supported by most of engine*/
#define C256 1
#define GRAYSCALE 2
#define TRUECOLOR16 4
#define TRUECOLOR24 8
#define TRUECOLOR 16
/*special mage types used internally by XaoS */
#define LARGEITER 32
#define SMALLITER 64

/*palette types handled by the dithering filter*/
#define LBITMAP 256
#define MBITMAP 512
#define LIBITMAP 1024
#define MIBITMAP 2048
#define FIXEDCOLOR 4096

#define ALLMASK (C256 | TRUECOLOR16 | TRUECOLOR24 | LARGEITER | GRAYSCALE)
#define BITMAPS (LBITMAP | MBITMAP | LIBITMAP | MIBITMAP)
#define MASK1BPP (SMALLITER | C256 | GRAYSCALE)
#define MASK2BPP (TRUECOLOR16 | LARGEITER)
#define MASK3BPP (TRUECOLOR24)
#define MASK4BPP (TRUECOLOR)

/*flags for requirements */
#define IMAGEDATA 1
#define TOUCHIMAGE 2
#define NEWIMAGE 4
/*flags for initdata */
#define DATALOST 1
/*flags for doit */
#define INTERRUPTIBLE 1
#define PALETTEONLY 2
/*return flags */
#define INEXACT 1
#define CHANGED 2
#define ANIMATION 4
#define INCOMPLETE (1 << 29)
/*flags for filters */
#define ALLOCEDIMAGE 1 /*used by inherimage mechanizm */
#define SHAREDDATA 2

#define PALGORITHMS 3
#include "pixel_t.h"
#define imgetpixel(image, x, y)                                                \
    ((image)->bytesperpixel == 1                                               \
         ? (image)->currlines[y][x]                                            \
         : ((image)->bytesperpixel == 4                                        \
                ? ((pixel32_t *)(image)->currlines[y])[x]                      \
                : (image)->bytesperpixel == 3                                  \
                      ? (((pixel16_t *)(image)->currlines[y])[x] +             \
                         ((image)->currlines[y][3 * (x) + 2] << 16))           \
                      : (((pixel16_t *)(image)->currlines[y])[x])))
struct requirements {
    int nimages;
    int supportedmask;
    int flags;
};
struct filter {
    struct filter *next, *previous;
    struct queue *queue;
    const struct filteraction *action;
    struct image *image, *childimage;
    struct requirements req;
    struct fractal_context *fractalc;
    void *data;
    const char *name;
    int flags;
    int imageversion; /*For detection whether image changed or not */
    void (*wait_function)(struct filter *f);
    /*stuff for wait_function */
    int pos, max, incalculation, readyforinterrupt, interrupt;
    const char *pass;
};
struct initdata {
    void (*wait_function)(struct filter *f);
    struct image *image;
    struct fractal_context *fractalc;
    int flags;
};
struct filteraction {
    const char *name;
    const char *shortname;
    int flags;
    struct filter *(*getinstance)(const struct filteraction *a);
    void (*destroyinstance)(struct filter *f);
    int (*doit)(struct filter *f, int flags, int time);
    int (*requirement)(struct filter *f, struct requirements *r);
    int (*initialize)(struct filter *f, struct initdata *i);
    void (*convertup)(struct filter *f, int *x, int *y);
    void (*convertdown)(struct filter *f, int *x, int *y);
    void (*removefilter)(struct filter *f);
};
struct queue {
    struct filter *first, *last;
    int isinitialized;
    struct filter *palettechg;
    struct image *saveimage;
};

#define datalost(f, i)                                                         \
    (((i)->flags & DATALOST) ||                                                \
     ((f)->imageversion && (f)->imageversion != (i)->image->version))
/*filter actions */

extern const struct filteraction interlace_filter, stereogram_filter,
    subwindow_filter, smalliter_filter, julia_filter, blur_filter, edge_filter,
    edge2_filter, rotate_filter, starfield_filter, truecolor_filter,
    fixedcolor_filter, bitmap_filter, emboss_filter, palette_filter,
    antialias_filter, threed_filter;

extern unsigned int col_diff[3][512];
struct filter *createfilter(const struct filteraction *fa);
struct queue *create_queue(struct filter *f);
void insertfilter(struct filter *f1, struct filter *f2);
void removefilter(struct filter *f);
void addfilter(struct filter *f1, struct filter *f2);
int initqueue(struct queue *q);

/*Filter utility functions */
int reqimage(struct filter *f, struct requirements *req, int supportedmask,
             int flags);
int inherimage(struct filter *f, struct initdata *data, int flags, int width,
               int height, struct palette *palette, float pixelwidth,
               float pixelheight);
void destroyinheredimage(struct filter *f);
void updateinheredimage(struct filter *f);

void inhermisc(struct filter *f, const struct initdata *i);

/*image actions */

void flipgeneric(struct image *img);
struct image *create_image_qt(int width, int height, struct palette *palette,
                              float pixelwidth, float pixelheight);
struct image *create_image_lines(int width, int height, int nimages,
                                 pixel_t **lines1, pixel_t **lines2,
                                 struct palette *palette,
                                 void (*flip)(struct image *img), int flags,
                                 float pixelwidth, float pixelheight);
struct image *create_image_cont(int width, int height, int scanlinesize,
                                int nimages, pixel_t *buf1, pixel_t *buf2,
                                struct palette *palette,
                                void (*flip)(struct image *img), int flags,
                                float pixelwidth, float pixelheight);
struct image *create_image_mem(int width, int height, int nimages,
                               struct palette *palette, float pixelwidth,
                               float pixelheight);
struct image *create_subimage(struct image *simg, int width, int height,
                              int nimages, struct palette *palette,
                              float pixelwidth, float pixelheight);

void destroy_image(struct image *img);
void clear_image(struct image *img);

/*palette */

int bytesperpixel(int type);
void bestfit_init(void);
struct palette *createpalette(
    int start, int end, int type, int flags, int maxentries,
    int (*alloccolor)(struct palette *pal, int init, int r, int g, int b),
    void (*setcolor)(struct palette *pal, int start, int end, rgb_t *rgb),
    void (*allocfinished)(struct palette *pal),
    void (*cyclecolors)(struct palette *pal, int direction),
    union paletteinfo *info);
void destroypalette(struct palette *palette);
int mkdefaultpalette(struct palette *palette);
int mkstereogrampalette(struct palette *palette);
int mkstarfieldpalette(struct palette *palette);
int mkblurpalette(struct palette *palette);
int mkgraypalette(struct palette *palette);
int mkrgb(struct palette *palette);
int mkpalette(struct palette *palette, int seed, int algorithm);
int shiftpalette(struct palette *palette, int n);
void preallocpalette(struct palette *pal);
struct palette *clonepalette(struct palette *palette);
void restorepalette(struct palette *dest, struct palette *src);
void convertupgeneric(struct filter *f, int *x, int *y);
void convertdowngeneric(struct filter *f, int *x, int *y);
int fixedalloccolor(struct palette *palette, int init, int r, int g, int b);
void getPaletteColor(struct palette *palette, int seed, int algorithm, int shift, int newColors[101][3]);
void getDEFSEGMENTColor(unsigned char newColors[][3]);
int mkcustompalette(struct palette *c, unsigned char newColors[27][3]);
void rgbtohex (int r, int g, int b, char color[6]);
void hextorgb (char *hexcolor, rgb_t color);

#define setfractalpalette(f, p)                                                \
    if ((f)->fractalc->palette == (f)->image->palette)                         \
    (f)->fractalc->palette = (p)

#ifdef STRUECOLOR24
#define TRUECOLOR24CASE(x)                                                     \
    case 3:                                                                    \
        x;                                                                     \
        break;
#else
#define TRUECOLOR24CASE(x)
#endif

#ifdef STRUECOLOR16
#define SUPPORT16
#endif
#ifdef SUPPORT16
#define TRUECOLOR16CASE(x)                                                     \
    case 2:                                                                    \
        x;                                                                     \
        break;
#else
#define TRUECOLOR16CASE(x)
#endif

#define drivercall(i, x1, x2, x3, x4)                                          \
    switch ((i).bytesperpixel) {                                               \
        TRUECOLOR24CASE(x3);                                                   \
        TRUECOLOR16CASE(x2);                                                   \
        case 1:                                                                \
            x1;                                                                \
            break;                                                             \
        case 4:                                                                \
            x4;                                                                \
    }
#ifdef SMBITMAPS
#define SBITMAPS
#else
#ifdef SLBITMAPS
#define SBITMAPS
#endif
#endif

#ifdef SBITMAPS
#define SCONVERTORS
#else
#ifdef SFIXEDCOLOR
#define SCONVERTORS
#endif
#endif

#include "formulas.h"
#endif
