]> bicyclesonthemoon.info Git - ott/enhance/blobdiff - core.c
fix index generating
[ott/enhance] / core.c
diff --git a/core.c b/core.c
index 948189840184b0ae5e9c29d33880a3c651ff0141..0a3d1c41da19d3670bb4bd1a238113063a1f8384 100644 (file)
--- a/core.c
+++ b/core.c
@@ -1,7 +1,7 @@
 /*
 core.c
 The tool with multiple enhancements and manipulations of pictures
-13.11.2022
+03.12.2022
 
 Copyright (C) 2014, 2015, 2022  Balthasar SzczepaƄski
 
@@ -36,11 +36,16 @@ on Pentium III libdevil must be recompiled with
 uint_fast16_t n_pictures = 0;
 struct Picture * picture;
 
-char INIT_FAILED[]   = "Failae to set up library.\n";
-char LOAD_FAILED[]   = "Failed to load picture.\n";
-char SAVE_FAILED[]   = "Failed to save picture.\n";
-char CREATE_FAILED[] = "Failed to create picture(s).\n";
-char CONVERT_FAILED[] = "Failed to convert picture.\n";
+char INIT_FAILED[]          = "Failed to set up library.\n";
+char LOAD_FAILED[]          = "Failed to load picture.\n";
+char SAVE_FAILED[]          = "Failed to save picture.\n";
+char CREATE_FAILED[]        = "Failed to create picture(s).\n";
+char CONVERT_FAILED[]       = "Failed to convert picture.\n";
+char SIZE_MISMATCH[]        = "Pictures have different sizes.\n";
+char MULTIPLE_FORBIDDEN[]   = "Picture is not allowed to have multiple frames.\n";
+char INDEXED_REQUIRED[]     = "Picture must be indexed.\n";
+char PALETTE_ONLY_REQUIRED[]= "Palette-only operation must be allowed.\n";
+char BAD_PALETTE_SIZE[]     = "Palette has wrong number of colours.\n";
 char NO_STR[] = "";
 
 int init (void)
@@ -70,6 +75,52 @@ ILuint get_handle (uint_fast16_t id)
                return -1;
 }
 
+int get_data (uint_fast16_t id, void **data, ILuint frame)
+{
+       if (id >= n_pictures)
+               return EINVAL;
+       if (!(picture[id].open))
+               return EIO;
+       ilBindImage(picture[id].handle);
+       if (frame > ilGetInteger(IL_NUM_IMAGES))
+               return EINVAL;
+       ilActiveImage(frame);
+       *data = ilGetData();
+       return 0;
+}
+
+int get_palette (uint_fast16_t id, void *palette, ILuint frame)
+{
+       if (id >= n_pictures)
+               return EINVAL;
+       if (!(picture[id].open))
+               return EIO;
+       ilBindImage(picture[id].handle);
+       if (frame > ilGetInteger(IL_NUM_IMAGES))
+               return EINVAL;
+       ilActiveImage(frame);
+       if (ilGetInteger(IL_IMAGE_FORMAT) != IL_COLOUR_INDEX)
+               return EINVAL;
+       *((void **)palette) = ilGetPalette();
+       return 0;
+}
+
+int set_palette (uint_fast16_t id, void *palette, ILuint size, ILenum type, ILuint frame)
+{
+       if (id >= n_pictures)
+               return EINVAL;
+       if (!(picture[id].open))
+               return EIO;
+       ilBindImage(picture[id].handle);
+       if (frame > ilGetInteger(IL_NUM_IMAGES))
+               return EINVAL;
+       ilActiveImage(frame);
+       if (ilGetInteger(IL_IMAGE_FORMAT) != IL_COLOUR_INDEX)
+               return EINVAL;
+       ilRegisterPal(palette, size, type);
+       return 0;
+}
+
 void create_picture (uint_fast16_t id)
 {
        if (id<n_pictures)
@@ -150,7 +201,7 @@ int reserve_pictures (uint_fast16_t n)
 
 void get_flags (struct IL_full_info *info, FLAG_TYPE *flags)
 {
-       *flags &= ~(HAS_ALPHA | IS_GRAY | IS_INDEXED | IS_MULTIPLE | IS_OVER_8BIT);
+       *flags &= ~(HAS_ALPHA | IS_GRAY | IS_INDEXED | IS_MULTIPLE | IS_OVER_8BIT | EFF_ALPHA | EFF_GRAY | EFF_INDEXED);
        
        if (info->num_images > 0)
                *flags |= IS_MULTIPLE;
@@ -161,7 +212,7 @@ void get_flags (struct IL_full_info *info, FLAG_TYPE *flags)
        switch (info->image_format)
        {
        case IL_COLOUR_INDEX:
-               *flags |= IS_INDEXED;
+               *flags |= IS_INDEXED | EFF_INDEXED;
                switch (info->palette_type)
                {
                case IL_PAL_BGR32:
@@ -170,7 +221,7 @@ void get_flags (struct IL_full_info *info, FLAG_TYPE *flags)
                        break;
                case IL_PAL_BGRA32:
                case IL_PAL_RGBA32:
-                       *flags |= HAS_ALPHA;
+                       *flags |= HAS_ALPHA | EFF_ALPHA;
                        break;
                case IL_PAL_RGB24:
                case IL_PAL_BGR24:
@@ -179,13 +230,13 @@ void get_flags (struct IL_full_info *info, FLAG_TYPE *flags)
                }
                break;
        case IL_LUMINANCE_ALPHA:
-               *flags |= HAS_ALPHA;
+               *flags |= HAS_ALPHA | EFF_ALPHA;
        case IL_LUMINANCE:
-               *flags |= IS_GRAY;
+               *flags |= IS_GRAY | EFF_GRAY;
                break;
        case IL_RGBA:
        case IL_BGRA:
-               *flags |= HAS_ALPHA;
+               *flags |= HAS_ALPHA | EFF_ALPHA;
        case IL_RGB:
        case IL_BGR:
        default:
@@ -207,6 +258,9 @@ int convert_picture (uint_fast16_t id, struct IL_full_info *info, FLAG_TYPE *fla
        if (id >= n_pictures)
                return EINVAL;
        
+       if (!(picture[id].open))
+               return EINVAL;
+       
        r = get_info (id, &info_c, 0);
        if (r!=0)
                return r;
@@ -221,7 +275,7 @@ int convert_picture (uint_fast16_t id, struct IL_full_info *info, FLAG_TYPE *fla
        {
                if (ilGetInteger(IL_NUM_IMAGES) > 0)
                {
-                       fputs("Picture is not allowed to have multiple frames.\n", stderr);
+                       fputs(MULTIPLE_FORBIDDEN, stderr);
                        return EINVAL;
                }
        }
@@ -326,7 +380,7 @@ int convert_picture (uint_fast16_t id, struct IL_full_info *info, FLAG_TYPE *fla
        {
                if (*flags & MUST_BE_INDEXED)
                {
-                       fputs("Picture must be indexed.\n", stderr);
+                       fputs(INDEXED_REQUIRED, stderr);
                        return EINVAL;
                }
                
@@ -450,6 +504,9 @@ int load_picture (uint_fast16_t id, char *path, struct IL_full_info *info, FLAG_
        if (id >= n_pictures)
                return EINVAL;
        
+       // if(!(picture[id].open))
+               create_picture(id);
+       
        ilBindImage(picture[id].handle);
        if (!ilLoadImage(path))
                return EIO;
@@ -457,6 +514,110 @@ int load_picture (uint_fast16_t id, char *path, struct IL_full_info *info, FLAG_
        return convert_picture(id, info, flags);
 }
 
+int load_picture_mem (uint_fast16_t id, const void *address, ILuint size, struct IL_full_info *info, FLAG_TYPE *flags)
+{
+       if (id >= n_pictures)
+               return EINVAL;
+       
+       // if(!(picture[id].open))
+               create_picture(id);
+       
+       ilBindImage(picture[id].handle);
+       if (!ilLoadL(IL_TYPE_UNKNOWN, address, size))
+               return EIO;
+       
+       return convert_picture(id, info, flags);
+}
+
+int build_picture (uint_fast16_t id, ILint width, ILint height, ILint frames, struct IL_full_info *info, FLAG_TYPE *flags)
+{
+       struct IL_full_info reference_info;
+       
+       reference_info.image_width = width;
+       reference_info.image_height = height;
+       if (*flags & CAN_BE_MULTIPLE)
+               reference_info.num_images = frames-1;
+       else
+               reference_info.num_images = 0;
+       
+       if (*flags & MUST_BE_INDEXED)
+       {
+               reference_info.image_format = IL_COLOUR_INDEX;
+               reference_info.image_type   = IL_UNSIGNED_BYTE;
+               reference_info.image_channels = 1;
+       }
+       else
+       {
+               if (*flags & MUST_BE_GRAY)
+               {
+                       if (*flags & MUST_HAVE_ALPHA)
+                       {
+                               reference_info.image_format = IL_LUMINANCE_ALPHA;
+                               reference_info.image_channels = 2;
+                       }
+                       else
+                       {
+                               reference_info.image_format = IL_LUMINANCE;
+                               reference_info.image_channels = 1;
+                       }
+               }
+               else
+               {
+                       if (*flags & MUST_HAVE_ALPHA)
+                       {
+                               reference_info.image_format = IL_RGBA;
+                               reference_info.image_channels = 4;
+                       }
+                       else
+                       {
+                               reference_info.image_format = IL_RGB;
+                               reference_info.image_channels = 3;
+                       }
+               }
+               if (*flags & CAN_BE_OVER_8BIT)
+                       reference_info.image_type   = IL_UNSIGNED_SHORT;
+               else
+                       reference_info.image_type   = IL_UNSIGNED_BYTE;
+       }
+       return build_picture_from_info(id, &reference_info, info, flags);
+}
+
+int build_picture_from_info (uint_fast16_t id, struct IL_full_info *reference_info, struct IL_full_info *info, FLAG_TYPE *flags)
+{
+       ILint i;
+       
+       if (id >= n_pictures)
+               return EINVAL;
+       
+       // if(!(picture[id].open))
+               create_picture(id);
+       
+       ilBindImage(picture[id].handle);
+       if (reference_info->num_images > 0)
+       {
+               if (!ilCreateSubImage(IL_SUB_NEXT, reference_info->num_images))
+                       return EIO;
+       }
+       
+       for (i=0; i<=reference_info->num_images; ++i)
+       {
+               ilBindImage(picture[id].handle);
+               ilActiveImage(i);
+               if (!ilTexImage(
+                       reference_info->image_width,
+                       reference_info->image_height,
+                       0,
+                       reference_info->image_channels,
+                       reference_info->image_format,
+                       reference_info->image_type,
+                       NULL
+               ))
+                       return EIO;
+       }
+       
+       return convert_picture (id, info, flags);
+}
+
 int save_picture (uint_fast16_t id, char *path, FLAG_TYPE flags)
 {
        int r;
@@ -536,391 +697,753 @@ int get_info (uint_fast16_t id, struct IL_full_info *info, ILint frame)
        return r;
 }
 
-int action_1picture (
+int perform_action_1picture (
        uint_fast16_t id,
-       ILint x0, ILint y0, ILint f0, ILint width, ILint height, ILint frames,
+       ILint x0, ILint y0, ILint f0,
+       ILint width, ILint height, ILint frames,
        ACTION_F *function,
        FLAG_TYPE flags,
        void *data
 )
 {
-       ILint actual_frames;
-       struct PixelInfo p;
+       return perform_action(
+               1,
+               &id,
+               &x0, &y0, &f0,
+               width, height, frames,
+               function,
+               &flags,
+               data
+       );
+}
+
+int perform_action_1picture_1pixel (
+       uint_fast16_t id,
+       ILint x, ILint y, ILint f,
+       ACTION_F *function,
+       FLAG_TYPE flags,
+       void *data
+)
+{
+       return perform_action_1picture (
+               id,
+               x, y, f,
+               1, 1, 1,
+               function,
+               flags | IN_WINDOW,
+               data
+       );
+}
+
+int perform_action (
+       uint_fast16_t n,
+       uint_fast16_t *id,
+       ILint *x0, ILint *y0, ILint *f0,
+       ILint width, ILint height, ILint frames,
+       ACTION_F *function,
+       FLAG_TYPE *flags,
+       void *data
+)
+{
+       uint_fast16_t i, j;
+       uint_fast8_t palette_only = 1;
+       uint_fast8_t skip_frame;
+       uint_fast8_t skip_line;
+       uint_fast8_t skip_pixel;
+       ILint actual_frames = 0x7FFFFFFF;
+       ILint actual_width  = 0x7FFFFFFF;
+       ILint actual_height = 0x7FFFFFFF;
+       ILint actual_colours= 0x7FFFFFFF;
+       ILint x, y, f;
+       struct PixelInfo *p;
        int r = 0;
        
-       p.flags = flags & ~(IS_MULTIPLE);
+       if (n==0)
+               return 0;
        
-       if (id >= n_pictures)
-               return EINVAL;
+       p = malloc((sizeof (struct PixelInfo)) * n);
+       if (p == NULL)
+       {
+               perror("perform_action(): malloc():");
+               return (r=errno);
+       }
        
-       ilBindImage(picture[id].handle);
-       get_info(id, &(p.info), 0);
+       for (i=0; i<n; ++i)
+       {
+               p[i].id = id[i];
+               p[i].handle = get_handle(p[i].id);
+               p[i].flags = flags[i] & ~(IS_MULTIPLE);
+               
+               if (p[i].id >= n_pictures)
+               {
+                       free(p);
+                       return EINVAL;
+               }
+               if (!(picture[p[i].id].open))
+               {
+                       free(p);
+                       return EINVAL;
+               }
+               
+               r = get_info(p[i].id, &(p[i].info), 0);
+               if (r!=0)
+                       return r;
+               p[i].frames = p[i].info.num_images + 1;
        
-       if (p.info.num_images > 0)
-               p.flags |= IS_MULTIPLE;
-       actual_frames = (p.flags & CAN_BE_MULTIPLE) ? (p.info.num_images+1) : 1;
+               if (p[i].frames > 0)
+                       p[i].flags |= IS_MULTIPLE;
+               if (p[i].flags & CAN_BE_MULTIPLE)
+               {
+                       if (p[i].frames < actual_frames)
+                               actual_frames = p[i].frames;
+               }
+               else
+                       actual_frames = 1;
+               
+               if (p[i].info.image_width < actual_width)
+                       actual_width = p[i].info.image_width;
+               if (p[i].info.image_height < actual_height)
+                       actual_height = p[i].info.image_height;
+               
+               if (*flags & IN_WINDOW) /* check first flags only! */
+               {
+                       p[i].f0 = f0[i];
+                       p[i].x0 = x0[i];
+                       p[i].y0 = y0[i];
+               }
+               else
+               {
+                       p[i].f0 = 0;
+                       p[i].x0 = 0;
+                       p[i].y0 = 0;
+               }
+               
+               if ((p[i].info.image_format != IL_COLOUR_INDEX) || (!(p[i].flags & OK_PALETTE_ONLY)))
+                       palette_only = 0;
+       }
        
-       if (!(flags & IN_WINDOW))
+       if (!(*flags & IN_WINDOW)) /* check first flags only! */
        {
-               f0 = 0;
+               width = actual_width;
+               height = actual_height;
                frames = actual_frames;
        }
        
-       for (
-               p.f_window = 0, p.f_pict = f0;
-               (p.f_window  < frames) && (r == 0);
-               ++(p.f_window), ++(p.f_pict)
-       ){
-               if ((p.f_pict >= 0) && (p.f_pict <= p.info.num_images))
-               {
-                       ilBindImage(picture[id].handle);
-                       ilActiveImage(p.f_pict);
-                       p.data = ilGetData();
-                       p.flags &= ~(IS_GRAY|IS_INDEXED|IS_OVER_8BIT|IS_PALETTE_ONLY);
+       for (f = 0; (f < frames) && (r == 0); ++f)
+       {
+               skip_frame = 0;
+               for (i=0; i<n; ++i)
+               {
+                       p[i].f_window = f;
+                       p[i].f_pict = p[i].f0 + f;
+                       if ((p[i].f_pict < 0) || (p[i].f_pict >= p[i].frames))
+                       {
+                               skip_frame = 1;
+                               break;
+                       }
                        
-                       p.alpha_offset = 0;
-                       p.value_offset = 0;
-                       p.index_offset = 0;
-                       p.red_offset = 0;
-                       p.green_offset = 0;
-                       p.blue_offset = 0;
+                       r = get_info(p[i].id, &(p[i].info), p[i].f_pict);
+                       if (r!=0)
+                               break;
+                       
+                       r = get_data(p[i].id, &(p[i].data),  p[i].f_pict);
+                       if (r!=0)
+                               break;
                        
-                       switch (p.info.image_format)
+                       p[i].flags &= ~(HAS_ALPHA|IS_GRAY|IS_INDEXED|IS_OVER_8BIT|IS_PALETTE_ONLY|EFF_ALPHA|EFF_GRAY|EFF_INDEXED);
+                       if (palette_only)
+                               p[i].flags |= IS_PALETTE_ONLY;
+                       
+                       p[i].alpha_offset = 0;
+                       p[i].value_offset = 0;
+                       p[i].index_offset = 0;
+                       p[i].red_offset = 0;
+                       p[i].green_offset = 0;
+                       p[i].blue_offset = 0;
+                       
+                       if (palette_only)
+                       {
+                               if (p[i].info.palette_num_cols < actual_colours)
+                                       actual_colours = p[i].info.palette_num_cols;
+                       }
+                       
+                       switch (p[i].info.image_format)
                        {
                        case IL_COLOUR_INDEX:
-                               p.flags |= IS_INDEXED;
-                               p.palette = ilGetPalette();
-                               switch (p.info.palette_type)
+                               p[i].flags |= IS_INDEXED | EFF_INDEXED;
+                               r = get_palette(p[i].id, &(p[i].palette),  p[i].f_pict);
+                               if (r!=0)
+                                       break;
+                               switch (p[i].info.palette_type)
                                {
                                case IL_PAL_BGR32:
                                case IL_PAL_RGB32:
-                                       p.flags |= IS_OVER_8BIT;
+                                       p[i].flags |= IS_OVER_8BIT;
                                        break;
                                case IL_PAL_BGRA32:
-                                       p.flags |= HAS_ALPHA;
-                                       p.alpha_offset = 3;
+                                       p[i].flags |= HAS_ALPHA | EFF_ALPHA;
+                                       p[i].alpha_offset = 3;
                                case IL_PAL_BGR24:
-                                       p.red_offset = 2;
-                                       p.green_offset = 1;
-                                       p.blue_offset = 0;
+                                       p[i].red_offset = 2;
+                                       p[i].green_offset = 1;
+                                       p[i].blue_offset = 0;
                                        break;
                                case IL_PAL_RGBA32:
-                                       p.flags |= HAS_ALPHA;
-                                       p.alpha_offset = 3;
+                                       p[i].flags |= HAS_ALPHA | EFF_ALPHA;
+                                       p[i].alpha_offset = 3;
                                case IL_PAL_RGB24:
                                default:
-                                       p.red_offset = 0;
-                                       p.green_offset = 1;
-                                       p.blue_offset = 2;
+                                       p[i].red_offset = 0;
+                                       p[i].green_offset = 1;
+                                       p[i].blue_offset = 2;
                                        break;
                                }
                                break;
                        case IL_LUMINANCE_ALPHA:
-                               p.flags |= HAS_ALPHA;
-                               p.alpha_offset = 1 * p.info.image_bpc;
+                               p[i].flags |= HAS_ALPHA | EFF_ALPHA;
+                               p[i].alpha_offset = 1 * p[i].info.image_bpc;
                        case IL_LUMINANCE:
-                               p.flags |= IS_GRAY;
-                               p.value_offset = 0 * p.info.image_bpc;
+                               p[i].flags |= IS_GRAY | EFF_GRAY;
+                               p[i].value_offset = 0 * p[i].info.image_bpc;
                                break;
                        case IL_BGRA:
-                               p.flags |= HAS_ALPHA;
-                               p.alpha_offset = 3 * p.info.image_bpc;
+                               p[i].flags |= HAS_ALPHA | EFF_ALPHA;
+                               p[i].alpha_offset = 3 * p[i].info.image_bpc;
                        case IL_BGR:
-                               p.red_offset = 2 * p.info.image_bpc;
-                               p.green_offset = 1 * p.info.image_bpc;
-                               p.blue_offset = 0 * p.info.image_bpc;
+                               p[i].red_offset   = 2 * p[i].info.image_bpc;
+                               p[i].green_offset = 1 * p[i].info.image_bpc;
+                               p[i].blue_offset  = 0 * p[i].info.image_bpc;
                                break;
                        case IL_RGBA:
-                               p.flags |= HAS_ALPHA;
-                               p.alpha_offset = 3 * p.info.image_bpc;
+                               p[i].flags |= HAS_ALPHA | EFF_ALPHA;
+                               p[i].alpha_offset = 3 * p[i].info.image_bpc;
                        case IL_RGB:
                        default:
-                               p.red_offset = 0 * p.info.image_bpc;
-                               p.green_offset = 1 * p.info.image_bpc;
-                               p.blue_offset = 2 * p.info.image_bpc;
+                               p[i].red_offset   = 0 * p[i].info.image_bpc;
+                               p[i].green_offset = 1 * p[i].info.image_bpc;
+                               p[i].blue_offset  = 2 * p[i].info.image_bpc;
                                break;
                        }
-                       if (p.info.image_bpc > 1)
-                               p.flags |= IS_OVER_8BIT;
+                       if (p[i].info.image_bpc > 1)
+                               p[i].flags |= IS_OVER_8BIT;
                        
-                       if ((p.flags & IS_INDEXED) && (p.flags & OK_PALETTE_ONLY))
+                       if ((p[i].flags & IS_INDEXED) && (p[i].flags & CANNOT_BE_INDEXED))
+                               p[i].flags &= ~EFF_INDEXED;
+                       if ((p[i].flags & IS_GRAY) && (p[i].flags & CANNOT_BE_GRAY))
+                               p[i].flags &= ~EFF_GRAY;
+                       else if (!(p[i].flags & IS_GRAY) && (p[i].flags & MUST_BE_GRAY))
+                               p[i].flags |= EFF_GRAY;
+                       if ((!(p[i].flags & HAS_ALPHA)) && (p[i].flags & MUST_HAVE_ALPHA))
+                               p[i].flags |= EFF_ALPHA;
+                       else if ((p[i].flags & HAS_ALPHA) && (p[i].flags & CANNOT_HAVE_ALPHA))
+                               p[i].flags &= ~EFF_ALPHA;
+               }
+               if (!skip_frame)
+               {
+                       if (palette_only)
                        {
-                               p.flags |= IS_PALETTE_ONLY;
-                               for (
-                                       p.index = 0, p.pal_offset=0;
-                                       (p.index < p.info.palette_num_cols) && (r == 0);
-                                       ++p.index, p.pal_offset += p.info.palette_bpp
-                               ){
-                                       if (!(p.flags & NOT_READABLE))
+                               for (i=0; i<n; ++i)
+                               {
+                                       p[i].index = 0;
+                                       p[i].pal_offset = 0;
+                                       flags[i] |= IS_PALETTE_ONLY;
+                               }
+                               for (j=0; (j<actual_colours) && (r==0); ++j)
+                               {
+                                       for (i=0; i<n; ++i)
                                        {
-                                               p.red   = (ILuint)(*((ILubyte*)(p.palette + p.pal_offset + p.red_offset)));
-                                               p.green = (ILuint)(*((ILubyte*)(p.palette + p.pal_offset + p.green_offset)));
-                                               p.blue  = (ILuint)(*((ILubyte*)(p.palette + p.pal_offset + p.blue_offset)));
-                                               if (p.flags & HAS_ALPHA)
+                                               if (!(p[i].flags & NOT_READABLE))
                                                {
-                                                       if (p.flags & CANNOT_HAVE_ALPHA)
-                                                               p.alpha = 0xFF;
-                                                       else
-                                                               p.alpha = (ILuint)(*((ILubyte*)(p.palette + p.pal_offset + p.alpha_offset)));
+                                                       p[i].red   = (ILuint)(*((ILubyte*)(p[i].palette + p[i].pal_offset + p[i].red_offset)));
+                                                       p[i].green = (ILuint)(*((ILubyte*)(p[i].palette + p[i].pal_offset + p[i].green_offset)));
+                                                       p[i].blue  = (ILuint)(*((ILubyte*)(p[i].palette + p[i].pal_offset + p[i].blue_offset)));
+                                                       if (p[i].flags & HAS_ALPHA)
+                                                       {
+                                                               if (p[i].flags & CANNOT_HAVE_ALPHA)
+                                                                       p[i].alpha = 0xFF;
+                                                               else
+                                                                       p[i].alpha = (ILuint)(*((ILubyte*)(p[i].palette + p[i].pal_offset + p[i].alpha_offset)));
+                                                       }
+                                                       else if (p[i].flags & MUST_HAVE_ALPHA)
+                                                               p[i].alpha = 0xFF;
+                                                       if (p[i].flags & MUST_BE_GRAY)
+                                                               p[i].value = (p[i].red + p[i].green + p[i].blue) / 3;
                                                }
-                                               else if (p.flags & MUST_HAVE_ALPHA)
-                                                       p.alpha = 0xFF;
-                                               if (p.flags & MUST_BE_GRAY)
-                                                       p.value = (p.red + p.green + p.blue) / 3;
                                        }
                                        
-                                       r = function(1, &p, data);
+                                       r = function(n, p, data);
                                        if (r)
                                                break;
                                        
-                                       if (!(p.flags & NOT_READABLE))
+                                       for (i=0; i<n; ++i)
                                        {
-                                               if (p.flags & MUST_BE_GRAY)
-                                               {
-                                                       p.red   = p.value;
-                                                       p.green = p.value;
-                                                       p.blue  = p.value;
-                                               }
-                                               if (p.flags & HAS_ALPHA)
+                                               if (!(p[i].flags & NOT_WRITABLE))
                                                {
-                                                       if (p.flags & CANNOT_HAVE_ALPHA)
-                                                               p.alpha = 0xFF;
-                                                       *((ILubyte*)(p.palette + p.pal_offset + p.alpha_offset)) = (ILubyte)p.alpha;
+                                                       if (p[i].flags & MUST_BE_GRAY)
+                                                       {
+                                                               p[i].red   = p[i].value;
+                                                               p[i].green = p[i].value;
+                                                               p[i].blue  = p[i].value;
+                                                       }
+                                                       if (p[i].flags & HAS_ALPHA)
+                                                       {
+                                                               if (p[i].flags & CANNOT_HAVE_ALPHA)
+                                                                       p[i].alpha = 0xFF;
+                                                               *((ILubyte*)(p[i].palette + p[i].pal_offset + p[i].alpha_offset)) = (ILubyte)(p[i].alpha);
+                                                       }
+                                                       *((ILubyte*)(p[i].palette + p[i].pal_offset + p[i].red_offset))   = (ILubyte)(p[i].red);
+                                                       *((ILubyte*)(p[i].palette + p[i].pal_offset + p[i].green_offset)) = (ILubyte)(p[i].green);
+                                                       *((ILubyte*)(p[i].palette + p[i].pal_offset + p[i].blue_offset))  = (ILubyte)(p[i].blue);
                                                }
-                                               *((ILubyte*)(p.palette + p.pal_offset + p.red_offset))   = (ILubyte)p.red;
-                                               *((ILubyte*)(p.palette + p.pal_offset + p.green_offset)) = (ILubyte)p.green;
-                                               *((ILubyte*)(p.palette + p.pal_offset + p.blue_offset))  = (ILubyte)p.blue;
+                                               
+                                               ++(p[i].index);
+                                               p[i].pal_offset += p[i].info.palette_bpp;
                                        }
                                }
                        }
                        else
                        {
-                               p.line_bytes = p.info.image_bytes_per_pixel * p.info.image_width;
-                               p.frame_bytes = p.line_bytes * p.info.image_height;
-                               
-                               if (p.info.image_origin == IL_ORIGIN_LOWER_LEFT)
+                               for (i=0; i<n; ++i)
                                {
-                                       p.line_start = p.frame_bytes - p.line_bytes;
-                                       p.line_bytes = 0 - p.line_bytes;
+                                       p[i].line_bytes = p[i].info.image_bytes_per_pixel * p[i].info.image_width;
+                                       p[i].frame_bytes = p[i].line_bytes * p[i].info.image_height;
+                                       
+                                       if (p[i].info.image_origin == IL_ORIGIN_LOWER_LEFT)
+                                       {
+                                               p[i].line_start = p[i].frame_bytes - p[i].line_bytes;
+                                               p[i].line_bytes = 0 - p[i].line_bytes;
+                                       }
+                                       else
+                                               p[i].line_start = 0;
+                                       
+                                       p[i].y_window = 0;
+                                       p[i].y_pict = p[i].y0;
+                                       p[i].line_offset = p[i].line_start + (p[i].y_pict * p[i].line_bytes);
                                }
-                               else
-                                       p.line_start = 0;
-                               
-                               if (!(flags & IN_WINDOW))
+                               for (y=0; (y<height)  && (r == 0); ++y)
                                {
-                                       x0 = 0;
-                                       y0 = 0;
-                                       width = p.info.image_width;
-                                       height = p.info.image_height;
-                               }
-
-                               for (
-                                       p.y_window = 0, p.y_pict = y0, p.line_offset = p.line_start + (p.y_pict * p.line_bytes);
-                                       (p.y_window < height) && (r == 0);
-                                       ++p.y_window, ++p.y_pict, p.line_offset += p.line_bytes
-                               ){
-                                       if ((p.y_pict >= 0) && (p.y_pict < p.info.image_height))
+                                       skip_line = 0;
+                                       for (i=0; i<n; ++i)
+                                       {
+                                               if ((p[i].y_pict < 0) || (p[i].y_pict >= p[i].info.image_height))
+                                               {
+                                                       skip_line = 1;
+                                                       break;
+                                               }
+                                               
+                                               p[i].x_window = 0;
+                                               p[i].x_pict = p[i].x0;
+                                               p[i].pixel_offset = p[i].line_offset + (p[i].x_pict * p[i].info.image_bytes_per_pixel);
+                                       }
+                                       if (!skip_line)
                                        {
-                                               for (
-                                                       p.x_window = 0, p.x_pict = x0, p.pixel_offset = p.line_offset + (p.x_pict * p.info.image_bytes_per_pixel);
-                                                       (p.x_window < width) && (r == 0);
-                                                       ++p.x_window, ++p.x_pict, p.pixel_offset += p.info.image_bytes_per_pixel
-                                               ){
-                                                       if ((p.x_pict >=0) && (p.x_pict < p.info.image_width))
+                                               for (x=0; x<width; ++x)
+                                               {
+                                                       skip_pixel = 0;
+                                                       for (i=0; i<n; ++i)
+                                                       {
+                                                               if ((p[i].x_pict < 0) || (p[i].x_pict >= p[i].info.image_width))
+                                                               {
+                                                                       skip_pixel = 1;
+                                                                       break;
+                                                               }
+                                                       }
+                                                       if (!skip_pixel)
                                                        {
-                                                               if (!(p.flags & NOT_READABLE))
+                                                               for (i=0; i<n; ++i)
                                                                {
-                                                                       /* can this be done better? think about it */
-                                                                       switch (p.info.image_type)
+                                                                       if (!(p[i].flags & NOT_READABLE))
                                                                        {
-                                                                       case IL_INT:
-                                                                       case IL_UNSIGNED_INT:
-                                                                               if(p.flags & IS_INDEXED)
-                                                                                       p.index = *((ILuint*)(p.data + p.pixel_offset + p.index_offset));
-                                                                               else
+                                                                               /* can this be done better? think about it */
+                                                                               switch (p[i].info.image_type)
                                                                                {
-                                                                                       if(p.flags & IS_GRAY)
-                                                                                               p.value = *((ILuint*)(p.data + p.pixel_offset + p.value_offset));
+                                                                               case IL_INT:
+                                                                               case IL_UNSIGNED_INT:
+                                                                                       if (p[i].flags & IS_INDEXED)
+                                                                                               p[i].index = *((ILuint*)(p[i].data + p[i].pixel_offset + p[i].index_offset));
                                                                                        else
                                                                                        {
-                                                                                               p.red   = *((ILuint*)(p.data + p.pixel_offset + p.red_offset));
-                                                                                               p.green = *((ILuint*)(p.data + p.pixel_offset + p.green_offset));
-                                                                                               p.blue  = *((ILuint*)(p.data + p.pixel_offset + p.blue_offset));
+                                                                                               if (p[i].flags & IS_GRAY)
+                                                                                                       p[i].value = *((ILuint*)(p[i].data + p[i].pixel_offset + p[i].value_offset));
+                                                                                               else
+                                                                                               {
+                                                                                                       p[i].red   = *((ILuint*)(p[i].data + p[i].pixel_offset + p[i].red_offset));
+                                                                                                       p[i].green = *((ILuint*)(p[i].data + p[i].pixel_offset + p[i].green_offset));
+                                                                                                       p[i].blue  = *((ILuint*)(p[i].data + p[i].pixel_offset + p[i].blue_offset));
+                                                                                               }
+                                                                                               if (p[i].flags & HAS_ALPHA)
+                                                                                                       p[i].alpha = *((ILuint*)(p[i].data + p[i].pixel_offset + p[i].alpha_offset));
                                                                                        }
-                                                                                       if(p.flags & HAS_ALPHA)
-                                                                                               p.alpha = *((ILuint*)(p.data + p.pixel_offset + p.alpha_offset));
-                                                                               }
-                                                                               break;
-                                                                       case IL_SHORT:
-                                                                       case IL_UNSIGNED_SHORT:
-                                                                               if(p.flags & IS_INDEXED)
-                                                                                       p.index = (ILuint)(*((ILushort*)(p.data + p.pixel_offset + p.index_offset)));
-                                                                               else
-                                                                               {
-                                                                                       if(p.flags & IS_GRAY)
-                                                                                               p.value = (ILuint)(*((ILushort*)(p.data + p.pixel_offset + p.value_offset)));
+                                                                                       break;
+                                                                               case IL_SHORT:
+                                                                               case IL_UNSIGNED_SHORT:
+                                                                                       if (p[i].flags & IS_INDEXED)
+                                                                                               p[i].index = (ILuint)(*((ILushort*)(p[i].data + p[i].pixel_offset + p[i].index_offset)));
                                                                                        else
                                                                                        {
-                                                                                               p.red   = (ILuint)(*((ILushort*)(p.data + p.pixel_offset + p.red_offset)));
-                                                                                               p.green = (ILuint)(*((ILushort*)(p.data + p.pixel_offset + p.green_offset)));
-                                                                                               p.blue  = (ILuint)(*((ILushort*)(p.data + p.pixel_offset + p.blue_offset)));
+                                                                                               if(p[i].flags & IS_GRAY)
+                                                                                                       p[i].value = (ILuint)(*((ILushort*)(p[i].data + p[i].pixel_offset + p[i].value_offset)));
+                                                                                               else
+                                                                                               {
+                                                                                                       p[i].red   = (ILuint)(*((ILushort*)(p[i].data + p[i].pixel_offset + p[i].red_offset)));
+                                                                                                       p[i].green = (ILuint)(*((ILushort*)(p[i].data + p[i].pixel_offset + p[i].green_offset)));
+                                                                                                       p[i].blue  = (ILuint)(*((ILushort*)(p[i].data + p[i].pixel_offset + p[i].blue_offset)));
+                                                                                               }
+                                                                                               if(p[i].flags & HAS_ALPHA)
+                                                                                                       p[i].alpha = (ILuint)(*((ILushort*)(p[i].data + p[i].pixel_offset + p[i].alpha_offset)));
                                                                                        }
-                                                                                       if(p.flags & HAS_ALPHA)
-                                                                                               p.alpha = (ILuint)(*((ILushort*)(p.data + p.pixel_offset + p.alpha_offset)));
-                                                                               }
-                                                                               break;
-                                                                       case IL_BYTE:
-                                                                       case IL_UNSIGNED_BYTE:
-                                                                               if(p.flags & IS_INDEXED)
-                                                                                       p.index = (ILuint)(*((ILubyte*)(p.data + p.pixel_offset + p.index_offset)));
-                                                                               else
-                                                                               {
-                                                                                       if(p.flags & IS_GRAY)
-                                                                                               p.value = (ILuint)(*((ILubyte*)(p.data + p.pixel_offset + p.value_offset)));
+                                                                                       break;
+                                                                               case IL_BYTE:
+                                                                               case IL_UNSIGNED_BYTE:
+                                                                                       if(p[i].flags & IS_INDEXED)
+                                                                                               p[i].index = (ILuint)(*((ILubyte*)(p[i].data + p[i].pixel_offset + p[i].index_offset)));
                                                                                        else
                                                                                        {
-                                                                                               p.red   = (ILuint)(*((ILubyte*)(p.data + p.pixel_offset + p.red_offset)));
-                                                                                               p.green = (ILuint)(*((ILubyte*)(p.data + p.pixel_offset + p.green_offset)));
-                                                                                               p.blue  = (ILuint)(*((ILubyte*)(p.data + p.pixel_offset + p.blue_offset)));
+                                                                                               if(p[i].flags & IS_GRAY)
+                                                                                                       p[i].value = (ILuint)(*((ILubyte*)(p[i].data + p[i].pixel_offset + p[i].value_offset)));
+                                                                                               else
+                                                                                               {
+                                                                                                       p[i].red   = (ILuint)(*((ILubyte*)(p[i].data + p[i].pixel_offset + p[i].red_offset)));
+                                                                                                       p[i].green = (ILuint)(*((ILubyte*)(p[i].data + p[i].pixel_offset + p[i].green_offset)));
+                                                                                                       p[i].blue  = (ILuint)(*((ILubyte*)(p[i].data + p[i].pixel_offset + p[i].blue_offset)));
+                                                                                               }
+                                                                                               if(p[i].flags & HAS_ALPHA)
+                                                                                                       p[i].alpha = (ILuint)(*((ILubyte*)(p[i].data + p[i].pixel_offset + p[i].alpha_offset)));
                                                                                        }
-                                                                                       if(p.flags & HAS_ALPHA)
-                                                                                               p.alpha = (ILuint)(*((ILubyte*)(p.data + p.pixel_offset + p.alpha_offset)));
+                                                                                       break;
+                                                                               default:
+                                                                                       break;
                                                                                }
-                                                                               break;
-                                                                       default:
-                                                                               break;
-                                                                       }
                                                                        
-                                                                       if ((p.flags & IS_INDEXED) && (p.flags & CANNOT_BE_INDEXED))
-                                                                       {
-                                                                               if (p.index < p.info.palette_num_cols)
+                                                                               if ((p[i].flags & IS_INDEXED) && (p[i].flags & CANNOT_BE_INDEXED))
                                                                                {
-                                                                                       p.pal_offset = p.index * p.info.palette_bpp;
-                                                                                       p.red   = (ILuint)(*((ILubyte*)(p.palette + p.pal_offset + p.red_offset)));
-                                                                                       p.green = (ILuint)(*((ILubyte*)(p.palette + p.pal_offset + p.green_offset)));
-                                                                                       p.blue  = (ILuint)(*((ILubyte*)(p.palette + p.pal_offset + p.blue_offset)));
-                                                                                       if (p.flags & HAS_ALPHA)
-                                                                                               p.alpha = (ILuint)(*((ILubyte*)(p.palette + p.pal_offset + p.alpha_offset)));
+                                                                                       if (p[i].index < p[i].info.palette_num_cols)
+                                                                                       {
+                                                                                               p[i].pal_offset = p[i].index * p[i].info.palette_bpp;
+                                                                                               p[i].red   = (ILuint)(*((ILubyte*)(p[i].palette + p[i].pal_offset + p[i].red_offset)));
+                                                                                               p[i].green = (ILuint)(*((ILubyte*)(p[i].palette + p[i].pal_offset + p[i].green_offset)));
+                                                                                               p[i].blue  = (ILuint)(*((ILubyte*)(p[i].palette + p[i].pal_offset + p[i].blue_offset)));
+                                                                                               if (p[i].flags & HAS_ALPHA)
+                                                                                                       p[i].alpha = (ILuint)(*((ILubyte*)(p[i].palette + p[i].pal_offset + p[i].alpha_offset)));
+                                                                                       }
                                                                                }
-                                                                       }
-                                                                       
-                                                                       if ((p.flags & IS_GRAY) && (p.flags & CANNOT_BE_GRAY))
-                                                                       {
-                                                                               p.red   = p.value;
-                                                                               p.green = p.value;
-                                                                               p.blue  = p.value;
-                                                                       }
-                                                                       else if (!(p.flags & IS_GRAY) && (p.flags & MUST_BE_GRAY))
-                                                                               p.value = (p.red + p.green + p.blue) / 3;
-                                                                       
-                                                                       if (((!(p.flags & HAS_ALPHA)) && (p.flags & MUST_HAVE_ALPHA)) || ((p.flags & HAS_ALPHA) && (p.flags & CANNOT_HAVE_ALPHA)))
-                                                                       {
-                                                                               switch (p.info.image_type)
+                                                                               
+                                                                               if ((p[i].flags & IS_GRAY) && (p[i].flags & CANNOT_BE_GRAY))
                                                                                {
-                                                                               case IL_INT:
-                                                                               case IL_UNSIGNED_INT:
-                                                                                       p.alpha = 0xFFFFFFFF;
-                                                                                       break;
-                                                                               case IL_SHORT:
-                                                                               case IL_UNSIGNED_SHORT:
-                                                                                       p.alpha = 0xFFFF;
-                                                                                       break;
-                                                                               case IL_BYTE:
-                                                                               case IL_UNSIGNED_BYTE:
-                                                                                       p.alpha = 0xFF;
-                                                                                       break;
-                                                                               default:
-                                                                                       break;
+                                                                                       p[i].red   = p[i].value;
+                                                                                       p[i].green = p[i].value;
+                                                                                       p[i].blue  = p[i].value;
                                                                                }
+                                                                               else if (!(p[i].flags & IS_GRAY) && (p[i].flags & MUST_BE_GRAY))
+                                                                                       p[i].value = (p[i].red + p[i].green + p[i].blue) / 3;
+                                                                               
+                                                                               if (((!(p[i].flags & HAS_ALPHA)) && (p[i].flags & MUST_HAVE_ALPHA)) || ((p[i].flags & HAS_ALPHA) && (p[i].flags & CANNOT_HAVE_ALPHA)))
+                                                                                       p[i].alpha = upscale_value(0xFF, p[i].info.image_bpc);
                                                                        }
                                                                }
                                                                
-                                                               r = function(1, &p, data);
+                                                               r = function(n, p, data);
                                                                if (r)
                                                                        break;
                                                                
-                                                               if (!(p.flags & NOT_WRITABLE))
+                                                               for (i=0; i<n; ++i)
                                                                {
-                                                                       if ((p.flags & HAS_ALPHA) && (p.flags & CANNOT_HAVE_ALPHA))
-                                                                               p.alpha = 0xFFFFFFFF;
-                                                                       
-                                                                       else if (!(p.flags & IS_GRAY) && (p.flags & MUST_BE_GRAY))
+                                                                       if (!(p[i].flags & NOT_WRITABLE))
                                                                        {
-                                                                               p.red   = p.value;
-                                                                               p.green = p.value;
-                                                                               p.blue  = p.value;
-                                                                       }
-                                                                       else if ((p.flags & IS_GRAY) && (p.flags & CANNOT_BE_GRAY))
-                                                                               p.value = (p.red + p.green + p.blue) / 3;
+                                                                               if ((p[i].flags & HAS_ALPHA) && (p[i].flags & CANNOT_HAVE_ALPHA))
+                                                                                       p[i].alpha = upscale_value(0xFF, p[i].info.image_bpc);
+                                                                               
+                                                                               else if (!(p[i].flags & IS_GRAY) && (p[i].flags & MUST_BE_GRAY))
+                                                                               {
+                                                                                       p[i].red   = p[i].value;
+                                                                                       p[i].green = p[i].value;
+                                                                                       p[i].blue  = p[i].value;
+                                                                               }
+                                                                               else if ((p[i].flags & IS_GRAY) && (p[i].flags & CANNOT_BE_GRAY))
+                                                                                       p[i].value = (p[i].red + p[i].green + p[i].blue) / 3;
                                                                        
-                                                                       switch (p.info.image_type)
-                                                                       {
-                                                                       case IL_INT:
-                                                                       case IL_UNSIGNED_INT:
-                                                                               if(p.flags & IS_INDEXED)
-                                                                                       *((ILuint*)(p.data + p.pixel_offset + p.index_offset)) = p.index;
-                                                                               else
+                                                                               switch (p[i].info.image_type)
                                                                                {
-                                                                                       if(p.flags & IS_GRAY)
-                                                                                               *((ILuint*)(p.data + p.pixel_offset + p.value_offset)) = p.value;
+                                                                               case IL_INT:
+                                                                               case IL_UNSIGNED_INT:
+                                                                                       if(p[i].flags & IS_INDEXED)
+                                                                                               *((ILuint*)(p[i].data + p[i].pixel_offset + p[i].index_offset)) = p[i].index;
                                                                                        else
                                                                                        {
-                                                                                               *((ILuint*)(p.data + p.pixel_offset + p.red_offset))   = p.red;
-                                                                                               *((ILuint*)(p.data + p.pixel_offset + p.green_offset)) = p.green;
-                                                                                               *((ILuint*)(p.data + p.pixel_offset + p.blue_offset))  = p.blue;
+                                                                                               if(p[i].flags & IS_GRAY)
+                                                                                                       *((ILuint*)(p[i].data + p[i].pixel_offset + p[i].value_offset)) = p[i].value;
+                                                                                               else
+                                                                                               {
+                                                                                                       *((ILuint*)(p[i].data + p[i].pixel_offset + p[i].red_offset))   = p[i].red;
+                                                                                                       *((ILuint*)(p[i].data + p[i].pixel_offset + p[i].green_offset)) = p[i].green;
+                                                                                                       *((ILuint*)(p[i].data + p[i].pixel_offset + p[i].blue_offset))  = p[i].blue;
+                                                                                               }
+                                                                                               if(p[i].flags & HAS_ALPHA)
+                                                                                                       *((ILuint*)(p[i].data + p[i].pixel_offset + p[i].alpha_offset)) = (ILubyte)p[i].alpha;
                                                                                        }
-                                                                                       if(p.flags & HAS_ALPHA)
-                                                                                               *((ILuint*)(p.data + p.pixel_offset + p.alpha_offset)) = (ILubyte)p.alpha;
-                                                                               }
-                                                                               break;
-                                                                       case IL_SHORT:
-                                                                       case IL_UNSIGNED_SHORT:
-                                                                               if(p.flags & IS_INDEXED)
-                                                                                       *((ILushort*)(p.data + p.pixel_offset + p.index_offset)) = (ILushort)p.index;
-                                                                               else
-                                                                               {
-                                                                                       if(p.flags & IS_GRAY)
-                                                                                               *((ILushort*)(p.data + p.pixel_offset + p.value_offset)) = (ILushort)p.value;
+                                                                                       break;
+                                                                               case IL_SHORT:
+                                                                               case IL_UNSIGNED_SHORT:
+                                                                                       if(p[i].flags & IS_INDEXED)
+                                                                                               *((ILushort*)(p[i].data + p[i].pixel_offset + p[i].index_offset)) = (ILushort)p[i].index;
                                                                                        else
                                                                                        {
-                                                                                               *((ILushort*)(p.data + p.pixel_offset + p.red_offset))   = (ILushort)p.red;
-                                                                                               *((ILushort*)(p.data + p.pixel_offset + p.green_offset)) = (ILushort)p.green;
-                                                                                               *((ILushort*)(p.data + p.pixel_offset + p.blue_offset))  = (ILushort)p.blue;
+                                                                                               if(p[i].flags & IS_GRAY)
+                                                                                                       *((ILushort*)(p[i].data + p[i].pixel_offset + p[i].value_offset)) = (ILushort)p[i].value;
+                                                                                               else
+                                                                                               {
+                                                                                                       *((ILushort*)(p[i].data + p[i].pixel_offset + p[i].red_offset))   = (ILushort)p[i].red;
+                                                                                                       *((ILushort*)(p[i].data + p[i].pixel_offset + p[i].green_offset)) = (ILushort)p[i].green;
+                                                                                                       *((ILushort*)(p[i].data + p[i].pixel_offset + p[i].blue_offset))  = (ILushort)p[i].blue;
+                                                                                               }
+                                                                                               if(p[i].flags & HAS_ALPHA)
+                                                                                                       *((ILushort*)(p[i].data + p[i].pixel_offset + p[i].alpha_offset)) = (ILushort)p[i].alpha;
                                                                                        }
-                                                                                       if(p.flags & HAS_ALPHA)
-                                                                                               *((ILushort*)(p.data + p.pixel_offset + p.alpha_offset)) = (ILushort)p.alpha;
-                                                                               }
-                                                                               break;
-                                                                       case IL_BYTE:
-                                                                       case IL_UNSIGNED_BYTE:
-                                                                               if(p.flags & IS_INDEXED)
-                                                                                       *((ILubyte*)(p.data + p.pixel_offset + p.index_offset)) = (ILubyte)p.index;
-                                                                               else
-                                                                               {
-                                                                                       if(p.flags & IS_GRAY)
-                                                                                               *((ILubyte*)(p.data + p.pixel_offset + p.value_offset)) = (ILubyte)p.value;
+                                                                                       break;
+                                                                               case IL_BYTE:
+                                                                               case IL_UNSIGNED_BYTE:
+                                                                                       if(p[i].flags & IS_INDEXED)
+                                                                                               *((ILubyte*)(p[i].data + p[i].pixel_offset + p[i].index_offset)) = (ILubyte)p[i].index;
                                                                                        else
                                                                                        {
-                                                                                               *((ILubyte*)(p.data + p.pixel_offset + p.red_offset))   = (ILubyte)p.red;
-                                                                                               *((ILubyte*)(p.data + p.pixel_offset + p.green_offset)) = (ILubyte)p.green;
-                                                                                               *((ILubyte*)(p.data + p.pixel_offset + p.blue_offset))  = (ILubyte)p.blue;
+                                                                                               if(p[i].flags & IS_GRAY)
+                                                                                                       *((ILubyte*)(p[i].data + p[i].pixel_offset + p[i].value_offset)) = (ILubyte)p[i].value;
+                                                                                               else
+                                                                                               {
+                                                                                                       *((ILubyte*)(p[i].data + p[i].pixel_offset + p[i].red_offset))   = (ILubyte)p[i].red;
+                                                                                                       *((ILubyte*)(p[i].data + p[i].pixel_offset + p[i].green_offset)) = (ILubyte)p[i].green;
+                                                                                                       *((ILubyte*)(p[i].data + p[i].pixel_offset + p[i].blue_offset))  = (ILubyte)p[i].blue;
+                                                                                               }
+                                                                                               if(p[i].flags & HAS_ALPHA)
+                                                                                                       *((ILubyte*)(p[i].data + p[i].pixel_offset + p[i].alpha_offset)) = (ILubyte)p[i].alpha;
                                                                                        }
-                                                                                       if(p.flags & HAS_ALPHA)
-                                                                                               *((ILubyte*)(p.data + p.pixel_offset + p.alpha_offset)) = (ILubyte)p.alpha;
+                                                                                       break;
+                                                                               default:
+                                                                                       break;
                                                                                }
-                                                                               break;
-                                                                       default:
-                                                                               break;
                                                                        }
                                                                }
                                                        }
+                                                       for (i=0; i<n; ++i)
+                                                       {
+                                                               ++(p[i].x_window);
+                                                               ++(p[i].x_pict);
+                                                               p[i].pixel_offset += p[i].info.image_bytes_per_pixel;
+                                                       }
                                                }
                                        }
+                                       for (i=0; i<n; ++i)
+                                       {
+                                               ++(p[i].y_window);
+                                               ++(p[i].y_pict);
+                                               p[i].line_offset += p[i].line_bytes;
+                                       }
                                }
                        }
                }
        }
        
+       free(p);
+       return r;
+}
+
+int perform_action_palette_mix (
+       uint_fast16_t *id,
+       ACTION_F *function,
+       FLAG_TYPE *flags,
+       void *data
+)
+{
+       uint_fast16_t i;
+       int r = 0;
+       struct PixelInfo p[3];
+       ILuint n_colors;
+       ILint actual_frames = 0x7FFFFFFF;
+       ILint f;
+       uint_fast8_t skip_frame;
+       ILuint j, k;
+       
+       
+       for (i=0; i<3; ++i)
+       {
+               p[i].id = id[i];
+               p[i].handle = get_handle(p[i].id);
+               p[i].flags = flags[i];
+               r = get_info(p[i].id, &(p[i].info), 0);
+               if (r!=0)
+                       return r;
+               get_flags(&(p[i].info), &(p[i].flags));
+               
+               if (!(p[i].flags & IS_INDEXED))
+               {
+                       fputs(INDEXED_REQUIRED, stderr);
+                       return EINVAL;
+               }
+               if (!(p[i].flags & OK_PALETTE_ONLY))
+               {
+                       fputs(PALETTE_ONLY_REQUIRED, stderr);
+                       return EINVAL;
+               }
+               if (p[i].flags & CAN_BE_MULTIPLE)
+               {
+                       if (p[i].info.num_images+1 < actual_frames)
+                               actual_frames = p[i].info.num_images+1;
+               }
+               else
+                       actual_frames = 1;
+               
+               p[i].f0 = 0;
+       }
+       
+       n_colors = p[0].info.palette_num_cols * p[1].info.palette_num_cols;
+       if (p[2].info.palette_num_cols < n_colors)
+       {
+               fputs(BAD_PALETTE_SIZE, stderr);
+               return EINVAL;
+       }
+       
+       for (f = 0; (f < actual_frames) && (r == 0); ++f)
+       {
+               skip_frame = 0;
+               for (i=0; i<3; ++i)
+               {
+                       p[i].f_window = f;
+                       p[i].f_pict = p[i].f0 + f;
+                       if ((p[i].f_pict < 0) || (p[i].f_pict > p[i].info.num_images))
+                       {
+                               skip_frame = 1;
+                               break;
+                       }
+                       
+                       r = get_info(p[i].id, &(p[i].info), p[i].f_pict);
+                       if (r!=0)
+                               return r;
+                       
+                       get_flags(&(p[i].info), &(p[i].flags));
+                       p[i].flags |= IS_PALETTE_ONLY;
+                       
+                       r = get_data(p[i].id, &(p[i].data),  p[i].f_pict);
+                       if (r!=0)
+                               return r;
+                       
+                       r = get_palette(p[i].id, &(p[i].palette),  p[i].f_pict);
+                       if (r!=0)
+                               return r;
+                       
+                       if (!(p[i].flags & IS_INDEXED))
+                       {
+                               fputs(INDEXED_REQUIRED, stderr);
+                               return EINVAL;
+                       }
+                       
+                       p[i].alpha_offset = 0;
+                       p[i].value_offset = 0;
+                       p[i].index_offset = 0;
+                       p[i].red_offset = 0;
+                       p[i].green_offset = 0;
+                       p[i].blue_offset = 0;
+                       
+                       switch (p[i].info.palette_type)
+                       {
+                       case IL_PAL_BGR32:
+                       case IL_PAL_RGB32:
+                               break;
+                       case IL_PAL_BGRA32:
+                               p[i].alpha_offset = 3;
+                       case IL_PAL_BGR24:
+                               p[i].red_offset = 2;
+                               p[i].green_offset = 1;
+                               p[i].blue_offset = 0;
+                               break;
+                       case IL_PAL_RGBA32:
+                               p[i].alpha_offset = 3;
+                       case IL_PAL_RGB24:
+                       default:
+                               p[i].red_offset = 0;
+                               p[i].green_offset = 1;
+                               p[i].blue_offset = 2;
+                               break;
+                       }
+                       
+                       if ((p[i].flags & IS_GRAY) && (p[i].flags & CANNOT_BE_GRAY))
+                               p[i].flags &= ~EFF_GRAY;
+                       else if (!(p[i].flags & IS_GRAY) && (p[i].flags & MUST_BE_GRAY))
+                               p[i].flags |= EFF_GRAY;
+                       if ((!(p[i].flags & HAS_ALPHA)) && (p[i].flags & MUST_HAVE_ALPHA))
+                               p[i].flags |= EFF_ALPHA;
+                       else if ((p[i].flags & HAS_ALPHA) && (p[i].flags & CANNOT_HAVE_ALPHA))
+                               p[i].flags &= ~EFF_ALPHA;
+               }
+               if (!skip_frame)
+               {
+                       p[0].index = 0;
+                       p[0].pal_offset = 0;
+                       p[2].index = 0;
+                       p[2].pal_offset = 0;
+                       for (j=0; j<p[0].info.palette_num_cols; ++j)
+                       {
+                               p[1].index = 0;
+                               p[1].pal_offset = 0;
+                               for (k=0; k<p[1].info.palette_num_cols; ++k)
+                               {
+                                       for (i=0; i<3; ++i)
+                                       {
+                                               if (!(p[i].flags & NOT_READABLE))
+                                               {
+                                                       p[i].red   = (ILuint)(*((ILubyte*)(p[i].palette + p[i].pal_offset + p[i].red_offset)));
+                                                       p[i].green = (ILuint)(*((ILubyte*)(p[i].palette + p[i].pal_offset + p[i].green_offset)));
+                                                       p[i].blue  = (ILuint)(*((ILubyte*)(p[i].palette + p[i].pal_offset + p[i].blue_offset)));
+                                                       if (p[i].flags & HAS_ALPHA)
+                                                       {
+                                                               if (p[i].flags & CANNOT_HAVE_ALPHA)
+                                                                       p[i].alpha = 0xFF;
+                                                               else
+                                                                       p[i].alpha = (ILuint)(*((ILubyte*)(p[i].palette + p[i].pal_offset + p[i].alpha_offset)));
+                                                       }
+                                                       else if (p[i].flags & MUST_HAVE_ALPHA)
+                                                               p[i].alpha = 0xFF;
+                                                       if (p[i].flags & MUST_BE_GRAY)
+                                                               p[i].value = (p[i].red + p[i].green + p[i].blue) / 3;
+                                               }
+                                       }
+                                       
+                                       r = function(3, p, data);
+                                       if (r)
+                                               break;
+                                       
+                                       for (i=0; i<3; ++i)
+                                       {
+                                               if (!(p[i].flags & NOT_WRITABLE))
+                                               {
+                                                       if (p[i].flags & MUST_BE_GRAY)
+                                                       {
+                                                               p[i].red   = p[i].value;
+                                                               p[i].green = p[i].value;
+                                                               p[i].blue  = p[i].value;
+                                                       }
+                                                       if (p[i].flags & HAS_ALPHA)
+                                                       {
+                                                               if (p[i].flags & CANNOT_HAVE_ALPHA)
+                                                                       p[i].alpha = 0xFF;
+                                                               *((ILubyte*)(p[i].palette + p[i].pal_offset + p[i].alpha_offset)) = (ILubyte)(p[i].alpha);
+                                                       }
+                                                       *((ILubyte*)(p[i].palette + p[i].pal_offset + p[i].red_offset))   = (ILubyte)(p[i].red);
+                                                       *((ILubyte*)(p[i].palette + p[i].pal_offset + p[i].green_offset)) = (ILubyte)(p[i].green);
+                                                       *((ILubyte*)(p[i].palette + p[i].pal_offset + p[i].blue_offset))  = (ILubyte)(p[i].blue);
+                                               }
+                                       }
+                                       
+                                       ++p[1].index;
+                                       p[1].pal_offset += p[1].info.palette_bpp;
+                                       ++p[2].index;
+                                       p[2].pal_offset += p[1].info.palette_bpp;
+                               }
+                               ++p[0].index;
+                               p[0].pal_offset += p[0].info.palette_bpp;
+                       }
+               }
+       }
+       
        return r;
 }
 
@@ -942,8 +1465,163 @@ ILubyte downscale_value (ILuint x, ILint bytes)
        return (ILubyte)(x >> (8 * (bytes-1)));
 }
 
+int copy_pixels (ILuint n, struct PixelInfo *p, void *data)
+{
+       ILuint max = *((ILuint *)data);
+       
+       if (n < 2)
+               return EINVAL;
+       
+       if (p[0].flags & EFF_INDEXED)
+       {
+               if (!(p[1].flags & EFF_INDEXED))
+                       return EINVAL;
+               p[1].index = p[0].index;
+       }
+       else if (p[0].flags & EFF_ALPHA)
+       {
+               if (p[0].flags & EFF_GRAY)
+               {
+                       if (!(p[1].flags & EFF_GRAY))
+                               return EINVAL;
+                       p[1].value = copy_1p_alpha(p[0].value, p[1].value, p[0].alpha, max);
+               }
+               else
+               {
+                       if (p[1].flags & EFF_GRAY)
+                               return EINVAL;
+                       p[1].red   = copy_1p_alpha(p[0].red,   p[1].red,   p[0].alpha, max);
+                       p[1].green = copy_1p_alpha(p[0].green, p[1].green, p[0].alpha, max);
+                       p[1].blue  = copy_1p_alpha(p[0].blue,  p[1].blue,  p[0].alpha, max);
+               }
+               if (p[1].flags & EFF_ALPHA)
+                       p[1].alpha = copy_1p_alpha(max, p[1].alpha, p[0].alpha, max);
+       }
+       else
+       {
+               if (p[0].flags & EFF_GRAY)
+               {
+                       if (!(p[1].flags & EFF_GRAY))
+                               return EINVAL;
+                       p[1].value = p[0].value;
+               }
+               else
+               {
+                       if (p[1].flags & EFF_GRAY)
+                               return EINVAL;
+                       p[1].red   = p[0].red;
+                       p[1].green = p[0].green;
+                       p[1].blue  = p[0].blue;
+               }
+               if (p[1].flags & EFF_ALPHA)
+                       p[1].alpha = max;
+       }
+       
+       return 0;
+}
+
+int fill_color (ILuint n, struct PixelInfo *p, void *data)
+{
+       struct ColorInfo *d = data;
+       
+       if (p->flags & EFF_INDEXED)
+       {
+               p->index = d->index;
+       }
+       else 
+       {
+               if (p->flags & EFF_ALPHA)
+                       p->alpha = d->alpha;
+               if (p->flags & EFF_GRAY)
+                       p->value = d->value;
+               else
+               {
+                       p->red   = d->red;
+                       p->green = d->green;
+                       p->blue  = d->blue;
+               }
+       }
+       
+       return 0;
+}
+
+ILuint copy_1p_alpha (ILint64 src, ILint64 dst, ILint64 alpha, ILint64 max)
+{
+       ILint64 v = (src * alpha + dst * (max - alpha)) / max;
+       return (ILuint)v;
+}
 
+int palette_mix_index (ILuint n, struct PixelInfo *p, void *data)
+{
+       if (n < 3)
+               return EIO;
+       
+       p[2].index = p[0].index * p[1].info.palette_num_cols + p[1].index;
+       
+       return 0;
+}
+
+ILuint tsqrt(ILuint s)
+{
+       switch(s)
+       {
+       case 256:
+               return 16;
+       case 225:
+               return 15;
+       case 196:
+               return 14;
+       case 169:
+               return 13;
+       case 144:
+               return 12;
+       case 121:
+               return 11;
+       case 100:
+               return 10;
+       case 81:
+               return 9;
+       case 64:
+               return 8;
+       case 49:
+               return 7;
+       case 36:
+               return 6;
+       case 25:
+               return 5;
+       case 16:
+               return 4;
+       case 9:
+               return 3;
+       case 4:
+               return 2;
+       case 1:
+               return 1;
+       default: // not a square - please fail
+               return 0;
+       }
+}
 
+//unsigned short isqrt(unsigned s)
+//{
+//      unsigned short r;
+//      unsigned short b=0x0040;
+//      
+//      while(b>s)
+//           b>>=2;
+//      while(b)
+//      {
+//           if(s>=r+b)
+//           {
+//                   s-=r+b;
+//                   r=(r>>1)+b;
+//           }
+//           else
+//                   r>>1;
+//           b>>2;
+//      }
+//      return r;
+//}
 
 
 // int action(