]> bicyclesonthemoon.info Git - ott/enhance/blob - core.c
Partial CGI update bacause git disagrees with me.
[ott/enhance] / core.c
1 /*
2 core.c
3 The tool with multiple enhancements and manipulations of pictures
4 03.12.2022
5
6 Copyright (C) 2014, 2015, 2022  Balthasar SzczepaƄski
7
8 This program is free software: you can redistribute it and/or modify
9 it under the terms of the GNU Affero General Public License as
10 published by the Free Software Foundation, either version 3 of the
11 License, or (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 GNU Affero General Public License for more details.
17
18 You should have received a copy of the GNU Affero General Public License
19 along with this program.  If not, see <http://www.gnu.org/licenses/>.
20
21
22 Requires Dev Image Library (libdevil) (http://openil.sourceforge.net/)
23 on Pentium III libdevil must be recompiled with
24 --disable-ssl2 --disable-ssl3
25 (https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=572954)
26 */
27
28 #include <stdlib.h>
29 #include <stdint.h>
30 #include <stdio.h>
31 #include <errno.h>
32
33 #include "core.h"
34
35
36 uint_fast16_t n_pictures = 0;
37 struct Picture * picture;
38
39 char INIT_FAILED[]          = "Failed to set up library.\n";
40 char LOAD_FAILED[]          = "Failed to load picture.\n";
41 char SAVE_FAILED[]          = "Failed to save picture.\n";
42 char CREATE_FAILED[]        = "Failed to create picture(s).\n";
43 char CONVERT_FAILED[]       = "Failed to convert picture.\n";
44 char SIZE_MISMATCH[]        = "Pictures have different sizes.\n";
45 char MULTIPLE_FORBIDDEN[]   = "Picture is not allowed to have multiple frames.\n";
46 char INDEXED_REQUIRED[]     = "Picture must be indexed.\n";
47 char PALETTE_ONLY_REQUIRED[]= "Palette-only operation must be allowed.\n";
48 char BAD_PALETTE_SIZE[]     = "Palette has wrong number of colours.\n";
49 char NO_STR[] = "";
50
51 int init (void)
52 {
53         ilInit();
54         if (!ilEnable(IL_FILE_OVERWRITE))
55                 return EIO;
56         // if(!ilEnable(IL_ORIGIN_SET))
57                 // return EIO;
58         // if(!ilOriginFunc(IL_ORIGIN_UPPER_LEFT))
59                 // return EIO;
60         return 0;
61 }
62
63 void finish (int const returnvalue, char const * const returntext)
64 {
65         clear_pictures();
66         fputs(returntext, stderr);
67         exit(returnvalue);
68 }
69
70 ILuint get_handle (uint_fast16_t id)
71 {
72         if (id<n_pictures)
73                 return picture[id].handle;
74         else  /* invalid but must return something */
75                 return -1;
76 }
77
78 int get_data (uint_fast16_t id, void **data, ILuint frame)
79 {
80         if (id >= n_pictures)
81                 return EINVAL;
82         if (!(picture[id].open))
83                 return EIO;
84         ilBindImage(picture[id].handle);
85         if (frame > ilGetInteger(IL_NUM_IMAGES))
86                 return EINVAL;
87         ilActiveImage(frame);
88         *data = ilGetData();
89         return 0;
90 }
91
92 int get_palette (uint_fast16_t id, void *palette, ILuint frame)
93 {
94         if (id >= n_pictures)
95                 return EINVAL;
96         if (!(picture[id].open))
97                 return EIO;
98         ilBindImage(picture[id].handle);
99         if (frame > ilGetInteger(IL_NUM_IMAGES))
100                 return EINVAL;
101         ilActiveImage(frame);
102         if (ilGetInteger(IL_IMAGE_FORMAT) != IL_COLOUR_INDEX)
103                 return EINVAL;
104         *((void **)palette) = ilGetPalette();
105         return 0;
106 }
107
108 int set_palette (uint_fast16_t id, void *palette, ILuint size, ILenum type, ILuint frame)
109 {
110         if (id >= n_pictures)
111                 return EINVAL;
112         if (!(picture[id].open))
113                 return EIO;
114         ilBindImage(picture[id].handle);
115         if (frame > ilGetInteger(IL_NUM_IMAGES))
116                 return EINVAL;
117         ilActiveImage(frame);
118         if (ilGetInteger(IL_IMAGE_FORMAT) != IL_COLOUR_INDEX)
119                 return EINVAL;
120         ilRegisterPal(palette, size, type);
121         return 0;
122 }
123
124 void create_picture (uint_fast16_t id)
125 {
126         if (id<n_pictures)
127         {
128                 if (picture[id].open)
129                         close_picture(id);
130                 ilGenImages(1, &(picture[id].handle));
131                 picture[id].open = 1;
132         }
133 }
134
135 int create_pictures (uint_fast16_t n)
136 {
137         uint_fast16_t i;
138         int r;
139         
140         r = reserve_pictures(n);
141         if (r)
142                 return r;
143         for (i=0; i<n_pictures; ++i)
144                 create_picture(i);
145         return 0;
146 }
147
148 void close_picture (uint_fast16_t id)
149 {
150         if (id<n_pictures)
151         {
152                 if (picture[id].open)
153                 {
154                         ilDeleteImages(1, &(picture[id].handle));
155                 }
156                 picture[id].open=0;
157         }
158 }
159
160 void close_pictures (void)
161 {
162         uint_fast16_t i;
163         for (i=0; i<n_pictures; ++i)
164                 close_picture(i);
165 }
166
167 void clear_pictures (void)
168 {
169         close_pictures();
170         if (n_pictures != 0)
171                 free(picture);
172         n_pictures = 0;
173 }
174
175 int reserve_pictures (uint_fast16_t n)
176 {
177         int r;
178         uint_fast16_t i;
179         
180         clear_pictures();
181         
182         if (n != 0)
183         {
184                 picture = malloc((sizeof (struct Picture)) * n);
185                 
186                 if (picture==NULL)
187                 {
188                         n_pictures = 0;
189                         perror("reserve_pictures(): malloc():");
190                         return (r=errno);
191                 }
192                 n_pictures = n;
193                 for(i=0; i<n_pictures; ++i)
194                 {
195                         picture[i].open = 0;
196                 }
197         }
198         
199         return 0;
200 }
201
202 void get_flags (struct IL_full_info *info, FLAG_TYPE *flags)
203 {
204         *flags &= ~(HAS_ALPHA | IS_GRAY | IS_INDEXED | IS_MULTIPLE | IS_OVER_8BIT | EFF_ALPHA | EFF_GRAY | EFF_INDEXED);
205         
206         if (info->num_images > 0)
207                 *flags |= IS_MULTIPLE;
208         
209         if (info->image_bpc > 1)
210                 *flags |= IS_OVER_8BIT;
211         
212         switch (info->image_format)
213         {
214         case IL_COLOUR_INDEX:
215                 *flags |= IS_INDEXED | EFF_INDEXED;
216                 switch (info->palette_type)
217                 {
218                 case IL_PAL_BGR32:
219                 case IL_PAL_RGB32:
220                         *flags |= IS_OVER_8BIT;
221                         break;
222                 case IL_PAL_BGRA32:
223                 case IL_PAL_RGBA32:
224                         *flags |= HAS_ALPHA | EFF_ALPHA;
225                         break;
226                 case IL_PAL_RGB24:
227                 case IL_PAL_BGR24:
228                 default:
229                         break;
230                 }
231                 break;
232         case IL_LUMINANCE_ALPHA:
233                 *flags |= HAS_ALPHA | EFF_ALPHA;
234         case IL_LUMINANCE:
235                 *flags |= IS_GRAY | EFF_GRAY;
236                 break;
237         case IL_RGBA:
238         case IL_BGRA:
239                 *flags |= HAS_ALPHA | EFF_ALPHA;
240         case IL_RGB:
241         case IL_BGR:
242         default:
243                 break;
244         }
245 }
246
247 int convert_picture (uint_fast16_t id, struct IL_full_info *info, FLAG_TYPE *flags)
248 {
249         ILint current_format;
250         ILint final_format;
251         ILint current_type;
252         ILint final_type;
253         ILint current_palette_type;
254         ILint final_palette_type;
255         struct IL_full_info info_c;
256         int r;
257         
258         if (id >= n_pictures)
259                 return EINVAL;
260         
261         if (!(picture[id].open))
262                 return EINVAL;
263         
264         r = get_info (id, &info_c, 0);
265         if (r!=0)
266                 return r;
267         
268         ilBindImage(picture[id].handle);
269         current_format = info_c.image_format;
270         final_format = current_format;
271         current_type = info_c.image_type;
272         final_type = current_type;
273         
274         if (!(*flags & CAN_BE_MULTIPLE))
275         {
276                 if (ilGetInteger(IL_NUM_IMAGES) > 0)
277                 {
278                         fputs(MULTIPLE_FORBIDDEN, stderr);
279                         return EINVAL;
280                 }
281         }
282         
283         if (final_format == IL_COLOUR_INDEX)
284         {
285                 current_palette_type = info_c.palette_type;
286                 final_palette_type = current_palette_type;
287                 
288                 if (*flags & (CANNOT_BE_INDEXED | MUST_BE_GRAY))
289                 {
290                         switch (final_palette_type)
291                         {
292                         case IL_PAL_BGR24:
293                                 final_format = IL_BGR;
294                                 final_type = IL_UNSIGNED_BYTE;
295                                 break;
296                         case IL_PAL_BGR32:
297                                 final_format = IL_BGR;
298                                 final_type = IL_UNSIGNED_SHORT;
299                                 break;
300                         case IL_PAL_BGRA32:
301                                 final_format = IL_BGRA;
302                                 final_type = IL_UNSIGNED_BYTE;
303                                 break;
304                         case IL_PAL_RGBA32:
305                                 final_format = IL_RGBA;
306                                 final_type = IL_UNSIGNED_BYTE;
307                                 break;
308                         case IL_PAL_RGB32:
309                                 final_format = IL_RGB;
310                                 final_type = IL_UNSIGNED_SHORT;
311                                 break;
312                         case IL_PAL_RGB24:
313                         default:
314                                 final_format = IL_RGB;
315                                 final_type = IL_UNSIGNED_BYTE;
316                                 break;
317                         }
318                 }
319                 else
320                 {
321                         if (!(*flags & CAN_BE_OVER_8BIT))
322                         {
323                                 switch (final_palette_type)
324                                 {
325                                 case IL_PAL_BGR32:
326                                         final_palette_type = IL_PAL_BGR24;
327                                         break;
328                                 case IL_PAL_RGB32:
329                                         final_palette_type = IL_PAL_RGB24;
330                                         break;
331                                 default:
332                                         break;
333                                 }
334                         }
335                         
336                         if (*flags & MUST_HAVE_ALPHA)
337                         {
338                                 switch (final_palette_type)
339                                 {
340                                 case IL_PAL_BGR24:
341                                 case IL_PAL_BGR32:
342                                         final_palette_type = IL_PAL_BGRA32;
343                                         break;
344                                 case IL_PAL_RGB24:
345                                 case IL_PAL_RGB32:
346                                         final_palette_type = IL_PAL_RGBA32;
347                                         break;
348                                 default:
349                                         break;
350                                 }
351                         }
352                         else if (*flags & CANNOT_HAVE_ALPHA)
353                         {
354                                 switch (final_palette_type)
355                                 {
356                                 case IL_PAL_BGRA32:
357                                         final_palette_type = IL_PAL_BGR24;
358                                         break;
359                                 case IL_PAL_RGBA32:
360                                         final_palette_type = IL_PAL_RGB24;
361                                         break;
362                                 default:
363                                         break;
364                                 }
365                         }
366                         
367                         if (final_palette_type != current_palette_type)
368                         {
369                                 fputs("Palette was converted!\n", stderr);
370                                 if (!(ilConvertPal(final_palette_type)))
371                                 {
372                                         fputs("Palette conversion failed.\n", stderr);
373                                         return EIO;
374                                 }
375                         }
376                 }
377         }
378         
379         if (final_format != IL_COLOUR_INDEX) /* might have changed */
380         {
381                 if (*flags & MUST_BE_INDEXED)
382                 {
383                         fputs(INDEXED_REQUIRED, stderr);
384                         return EINVAL;
385                 }
386                 
387                 if (*flags & MUST_HAVE_ALPHA)
388                 {
389                         switch (final_format)
390                         {
391                         case IL_RGB:
392                                 final_format = IL_RGBA;
393                                 break;
394                         case IL_BGR:
395                                 final_format = IL_BGRA;
396                                 break;
397                         case IL_LUMINANCE:
398                                 final_format = IL_LUMINANCE_ALPHA;
399                                 break;
400                         default:
401                                 break;
402                         }
403                 }
404                 else if (*flags & CANNOT_HAVE_ALPHA)
405                 {
406                         switch (final_format)
407                         {
408                         case IL_RGBA:
409                                 final_format = IL_RGB;
410                                 break;
411                         case IL_BGRA:
412                                 final_format = IL_BGR;
413                                 break;
414                         case IL_LUMINANCE_ALPHA:
415                                 final_format = IL_LUMINANCE;
416                                 break;
417                         default:
418                                 break;
419                         }
420                 }
421                 
422                 if (*flags & MUST_BE_GRAY)
423                 {
424                         switch (final_format)
425                         {
426                         case IL_RGB:
427                         case IL_BGR:
428                                 final_format = IL_LUMINANCE;
429                                 break;
430                         case IL_RGBA:
431                         case IL_BGRA:
432                                 final_format = IL_LUMINANCE_ALPHA;
433                                 break;
434                         default:
435                                 break;
436                         }
437                 }
438                 else if (*flags & CANNOT_BE_GRAY)
439                 {
440                         switch (final_format)
441                         {
442                         case IL_LUMINANCE:
443                                 final_format = IL_RGB;
444                                 break;
445                         case IL_LUMINANCE_ALPHA:
446                                 final_format = IL_RGBA;
447                                 break;
448                         default:
449                                 break;
450                         }
451                 }
452                 
453                 if (*flags & CAN_BE_OVER_8BIT)
454                 {
455                         switch(final_type)
456                         {
457                         case IL_BYTE:
458                                 final_type = IL_UNSIGNED_BYTE;
459                                 break;
460                         case IL_SHORT:
461                                 final_type = IL_UNSIGNED_SHORT;
462                                 break;
463                         case IL_DOUBLE:
464                         case IL_FLOAT:
465                         case IL_INT:
466                                 final_type = IL_UNSIGNED_INT;
467                                 break;
468                         default:
469                                 break;
470                         }
471                 }
472                 else
473                         final_type = IL_UNSIGNED_BYTE;
474                 
475                 if ((final_type != current_type) || (final_format != current_format))
476                 {
477                         fputs("Picture is converted!\n", stderr);
478                         if (!(ilConvertImage(final_format, final_type)))
479                         {
480                                 fputs("Image format conversion failed.\n", stderr);
481                                 return EIO;
482                         }
483                 }
484         }
485         if (info == NULL)
486         {
487                 r = get_info (id, &info_c, 0);
488                 if (r!=0)
489                         return r;
490                 get_flags(&info_c, flags);
491         }
492         else
493         {
494                 r = get_info (id, info, 0);
495                 if (r!=0)
496                         return r;
497                 get_flags(info, flags);
498         }
499         return 0;
500 }
501
502 int load_picture (uint_fast16_t id, char *path, struct IL_full_info *info, FLAG_TYPE *flags)
503 {
504         if (id >= n_pictures)
505                 return EINVAL;
506         
507         // if(!(picture[id].open))
508                 create_picture(id);
509         
510         ilBindImage(picture[id].handle);
511         if (!ilLoadImage(path))
512                 return EIO;
513         
514         return convert_picture(id, info, flags);
515 }
516
517 int load_picture_mem (uint_fast16_t id, const void *address, ILuint size, struct IL_full_info *info, FLAG_TYPE *flags)
518 {
519         if (id >= n_pictures)
520                 return EINVAL;
521         
522         // if(!(picture[id].open))
523                 create_picture(id);
524         
525         ilBindImage(picture[id].handle);
526         if (!ilLoadL(IL_TYPE_UNKNOWN, address, size))
527                 return EIO;
528         
529         return convert_picture(id, info, flags);
530 }
531
532 int build_picture (uint_fast16_t id, ILint width, ILint height, ILint frames, struct IL_full_info *info, FLAG_TYPE *flags)
533 {
534         struct IL_full_info reference_info;
535         
536         reference_info.image_width = width;
537         reference_info.image_height = height;
538         if (*flags & CAN_BE_MULTIPLE)
539                 reference_info.num_images = frames-1;
540         else
541                 reference_info.num_images = 0;
542         
543         if (*flags & MUST_BE_INDEXED)
544         {
545                 reference_info.image_format = IL_COLOUR_INDEX;
546                 reference_info.image_type   = IL_UNSIGNED_BYTE;
547                 reference_info.image_channels = 1;
548         }
549         else
550         {
551                 if (*flags & MUST_BE_GRAY)
552                 {
553                         if (*flags & MUST_HAVE_ALPHA)
554                         {
555                                 reference_info.image_format = IL_LUMINANCE_ALPHA;
556                                 reference_info.image_channels = 2;
557                         }
558                         else
559                         {
560                                 reference_info.image_format = IL_LUMINANCE;
561                                 reference_info.image_channels = 1;
562                         }
563                 }
564                 else
565                 {
566                         if (*flags & MUST_HAVE_ALPHA)
567                         {
568                                 reference_info.image_format = IL_RGBA;
569                                 reference_info.image_channels = 4;
570                         }
571                         else
572                         {
573                                 reference_info.image_format = IL_RGB;
574                                 reference_info.image_channels = 3;
575                         }
576                 }
577                 if (*flags & CAN_BE_OVER_8BIT)
578                         reference_info.image_type   = IL_UNSIGNED_SHORT;
579                 else
580                         reference_info.image_type   = IL_UNSIGNED_BYTE;
581         }
582         return build_picture_from_info(id, &reference_info, info, flags);
583 }
584
585 int build_picture_from_info (uint_fast16_t id, struct IL_full_info *reference_info, struct IL_full_info *info, FLAG_TYPE *flags)
586 {
587         ILint i;
588         
589         if (id >= n_pictures)
590                 return EINVAL;
591         
592         // if(!(picture[id].open))
593                 create_picture(id);
594         
595         ilBindImage(picture[id].handle);
596         if (reference_info->num_images > 0)
597         {
598                 if (!ilCreateSubImage(IL_SUB_NEXT, reference_info->num_images))
599                         return EIO;
600         }
601         
602         for (i=0; i<=reference_info->num_images; ++i)
603         {
604                 ilBindImage(picture[id].handle);
605                 ilActiveImage(i);
606                 if (!ilTexImage(
607                         reference_info->image_width,
608                         reference_info->image_height,
609                         0,
610                         reference_info->image_channels,
611                         reference_info->image_format,
612                         reference_info->image_type,
613                         NULL
614                 ))
615                         return EIO;
616         }
617         
618         return convert_picture (id, info, flags);
619 }
620
621 int save_picture (uint_fast16_t id, char *path, FLAG_TYPE flags)
622 {
623         int r;
624         
625         if (id >= n_pictures)
626                 return EINVAL;
627         
628         ilBindImage(picture[id].handle);
629         r = convert_picture(id, NULL, &flags);
630         if (r)
631                 return r;
632         
633         if (!ilSaveImage(path))
634                 return EIO;
635         return 0;
636 }
637
638 int get_info (uint_fast16_t id, struct IL_full_info *info, ILint frame)
639 {
640         int r = 0;
641         
642         if (id < n_pictures)
643         {
644                 ilBindImage(picture[id].handle);
645                 if (frame <= ilGetInteger(IL_NUM_IMAGES))
646                 {
647                         if(!ilActiveImage(frame))
648                                 r = EIO;
649                 }
650         }
651         
652         info->active_image          = ilGetInteger(IL_ACTIVE_IMAGE);
653         info->active_layer          = ilGetInteger(IL_ACTIVE_LAYER);
654         info->active_mipmap         = ilGetInteger(IL_ACTIVE_MIPMAP);
655         info->blit_blend            = ilGetInteger(IL_BLIT_BLEND);
656         info->compress_mode         = ilGetInteger(IL_COMPRESS_MODE);
657         info->conv_pal              = ilGetInteger(IL_CONV_PAL);
658         info->cur_image             = ilGetInteger(IL_CUR_IMAGE);
659         info->default_on_fail       = ilGetInteger(IL_DEFAULT_ON_FAIL);
660         info->dxtc_data_format      = ilGetInteger(IL_DXTC_DATA_FORMAT);
661         info->file_mode             = ilGetInteger(IL_FILE_MODE);
662         info->format_mode           = ilGetInteger(IL_FORMAT_MODE);
663         info->format_set            = ilGetInteger(IL_FORMAT_SET);
664         info->image_bits_per_pixel  = ilGetInteger(IL_IMAGE_BITS_PER_PIXEL);
665         info->image_bpc             = ilGetInteger(IL_IMAGE_BPC);
666         info->image_bytes_per_pixel = ilGetInteger(IL_IMAGE_BYTES_PER_PIXEL);
667         info->image_channels        = ilGetInteger(IL_IMAGE_CHANNELS);
668         info->image_cubeflags       = ilGetInteger(IL_IMAGE_CUBEFLAGS);
669         info->image_depth           = ilGetInteger(IL_IMAGE_DEPTH);
670         info->image_duration        = ilGetInteger(IL_IMAGE_DURATION);
671         info->image_format          = ilGetInteger(IL_IMAGE_FORMAT);
672         info->image_height          = ilGetInteger(IL_IMAGE_HEIGHT);
673         info->image_offx            = ilGetInteger(IL_IMAGE_OFFX);
674         info->image_offy            = ilGetInteger(IL_IMAGE_OFFY);
675         info->image_origin          = ilGetInteger(IL_IMAGE_ORIGIN);
676         info->image_planesize       = ilGetInteger(IL_IMAGE_PLANESIZE);
677         info->image_size_of_data    = ilGetInteger(IL_IMAGE_SIZE_OF_DATA);
678         info->image_type            = ilGetInteger(IL_IMAGE_TYPE);
679         info->image_width           = ilGetInteger(IL_IMAGE_WIDTH);
680         info->keep_dxtc_data        = ilGetInteger(IL_KEEP_DXTC_DATA);
681         info->num_faces             = ilGetInteger(IL_NUM_FACES);
682         info->num_images            = ilGetInteger(IL_NUM_IMAGES);
683         info->num_layers            = ilGetInteger(IL_NUM_LAYERS);
684         info->num_mipmaps           = ilGetInteger(IL_NUM_MIPMAPS);
685         info->origin_mode           = ilGetInteger(IL_ORIGIN_MODE);
686         info->origin_set            = ilGetInteger(IL_ORIGIN_SET);
687         info->palette_base_type     = ilGetInteger(IL_PALETTE_BASE_TYPE);
688         info->palette_bpp           = ilGetInteger(IL_PALETTE_BPP);
689         info->palette_num_cols      = ilGetInteger(IL_PALETTE_NUM_COLS);
690         info->palette_type          = ilGetInteger(IL_PALETTE_TYPE);
691         info->type_mode             = ilGetInteger(IL_TYPE_MODE);
692         info->type_set              = ilGetInteger(IL_TYPE_SET);
693         info->use_key_colour        = ilGetInteger(IL_USE_KEY_COLOUR);
694         info->version_num           = ilGetInteger(IL_VERSION_NUM);
695         
696
697         return r;
698 }
699
700 int perform_action_1picture (
701         uint_fast16_t id,
702         ILint x0, ILint y0, ILint f0,
703         ILint width, ILint height, ILint frames,
704         ACTION_F *function,
705         FLAG_TYPE flags,
706         void *data
707 )
708 {
709         return perform_action(
710                 1,
711                 &id,
712                 &x0, &y0, &f0,
713                 width, height, frames,
714                 function,
715                 &flags,
716                 data
717         );
718 }
719
720 int perform_action_1picture_1pixel (
721         uint_fast16_t id,
722         ILint x, ILint y, ILint f,
723         ACTION_F *function,
724         FLAG_TYPE flags,
725         void *data
726 )
727 {
728         return perform_action_1picture (
729                 id,
730                 x, y, f,
731                 1, 1, 1,
732                 function,
733                 flags | IN_WINDOW,
734                 data
735         );
736 }
737
738 int perform_action (
739         uint_fast16_t n,
740         uint_fast16_t *id,
741         ILint *x0, ILint *y0, ILint *f0,
742         ILint width, ILint height, ILint frames,
743         ACTION_F *function,
744         FLAG_TYPE *flags,
745         void *data
746 )
747 {
748         uint_fast16_t i, j;
749         uint_fast8_t palette_only = 1;
750         uint_fast8_t skip_frame;
751         uint_fast8_t skip_line;
752         uint_fast8_t skip_pixel;
753         ILint actual_frames = 0x7FFFFFFF;
754         ILint actual_width  = 0x7FFFFFFF;
755         ILint actual_height = 0x7FFFFFFF;
756         ILint actual_colours= 0x7FFFFFFF;
757         ILint x, y, f;
758         struct PixelInfo *p;
759         int r = 0;
760         
761         if (n==0)
762                 return 0;
763         
764         p = malloc((sizeof (struct PixelInfo)) * n);
765         if (p == NULL)
766         {
767                 perror("perform_action(): malloc():");
768                 return (r=errno);
769         }
770         
771         for (i=0; i<n; ++i)
772         {
773                 p[i].id = id[i];
774                 p[i].handle = get_handle(p[i].id);
775                 p[i].flags = flags[i] & ~(IS_MULTIPLE);
776                 
777                 if (p[i].id >= n_pictures)
778                 {
779                         free(p);
780                         return EINVAL;
781                 }
782                 if (!(picture[p[i].id].open))
783                 {
784                         free(p);
785                         return EINVAL;
786                 }
787                 
788                 r = get_info(p[i].id, &(p[i].info), 0);
789                 if (r!=0)
790                         return r;
791                 p[i].frames = p[i].info.num_images + 1;
792         
793                 if (p[i].frames > 0)
794                         p[i].flags |= IS_MULTIPLE;
795                 if (p[i].flags & CAN_BE_MULTIPLE)
796                 {
797                         if (p[i].frames < actual_frames)
798                                 actual_frames = p[i].frames;
799                 }
800                 else
801                         actual_frames = 1;
802                 
803                 if (p[i].info.image_width < actual_width)
804                         actual_width = p[i].info.image_width;
805                 if (p[i].info.image_height < actual_height)
806                         actual_height = p[i].info.image_height;
807                 
808                 if (*flags & IN_WINDOW) /* check first flags only! */
809                 {
810                         p[i].f0 = f0[i];
811                         p[i].x0 = x0[i];
812                         p[i].y0 = y0[i];
813                 }
814                 else
815                 {
816                         p[i].f0 = 0;
817                         p[i].x0 = 0;
818                         p[i].y0 = 0;
819                 }
820                 
821                 if ((p[i].info.image_format != IL_COLOUR_INDEX) || (!(p[i].flags & OK_PALETTE_ONLY)))
822                         palette_only = 0;
823         }
824         
825         if (!(*flags & IN_WINDOW)) /* check first flags only! */
826         {
827                 width = actual_width;
828                 height = actual_height;
829                 frames = actual_frames;
830         }
831         
832         for (f = 0; (f < frames) && (r == 0); ++f)
833         {
834                 skip_frame = 0;
835                 for (i=0; i<n; ++i)
836                 {
837                         p[i].f_window = f;
838                         p[i].f_pict = p[i].f0 + f;
839                         if ((p[i].f_pict < 0) || (p[i].f_pict >= p[i].frames))
840                         {
841                                 skip_frame = 1;
842                                 break;
843                         }
844                         
845                         r = get_info(p[i].id, &(p[i].info), p[i].f_pict);
846                         if (r!=0)
847                                 break;
848                         
849                         r = get_data(p[i].id, &(p[i].data),  p[i].f_pict);
850                         if (r!=0)
851                                 break;
852                         
853                         p[i].flags &= ~(HAS_ALPHA|IS_GRAY|IS_INDEXED|IS_OVER_8BIT|IS_PALETTE_ONLY|EFF_ALPHA|EFF_GRAY|EFF_INDEXED);
854                         if (palette_only)
855                                 p[i].flags |= IS_PALETTE_ONLY;
856                         
857                         p[i].alpha_offset = 0;
858                         p[i].value_offset = 0;
859                         p[i].index_offset = 0;
860                         p[i].red_offset = 0;
861                         p[i].green_offset = 0;
862                         p[i].blue_offset = 0;
863                         
864                         if (palette_only)
865                         {
866                                 if (p[i].info.palette_num_cols < actual_colours)
867                                         actual_colours = p[i].info.palette_num_cols;
868                         }
869                         
870                         switch (p[i].info.image_format)
871                         {
872                         case IL_COLOUR_INDEX:
873                                 p[i].flags |= IS_INDEXED | EFF_INDEXED;
874                                 r = get_palette(p[i].id, &(p[i].palette),  p[i].f_pict);
875                                 if (r!=0)
876                                         break;
877                                 switch (p[i].info.palette_type)
878                                 {
879                                 case IL_PAL_BGR32:
880                                 case IL_PAL_RGB32:
881                                         p[i].flags |= IS_OVER_8BIT;
882                                         break;
883                                 case IL_PAL_BGRA32:
884                                         p[i].flags |= HAS_ALPHA | EFF_ALPHA;
885                                         p[i].alpha_offset = 3;
886                                 case IL_PAL_BGR24:
887                                         p[i].red_offset = 2;
888                                         p[i].green_offset = 1;
889                                         p[i].blue_offset = 0;
890                                         break;
891                                 case IL_PAL_RGBA32:
892                                         p[i].flags |= HAS_ALPHA | EFF_ALPHA;
893                                         p[i].alpha_offset = 3;
894                                 case IL_PAL_RGB24:
895                                 default:
896                                         p[i].red_offset = 0;
897                                         p[i].green_offset = 1;
898                                         p[i].blue_offset = 2;
899                                         break;
900                                 }
901                                 break;
902                         case IL_LUMINANCE_ALPHA:
903                                 p[i].flags |= HAS_ALPHA | EFF_ALPHA;
904                                 p[i].alpha_offset = 1 * p[i].info.image_bpc;
905                         case IL_LUMINANCE:
906                                 p[i].flags |= IS_GRAY | EFF_GRAY;
907                                 p[i].value_offset = 0 * p[i].info.image_bpc;
908                                 break;
909                         case IL_BGRA:
910                                 p[i].flags |= HAS_ALPHA | EFF_ALPHA;
911                                 p[i].alpha_offset = 3 * p[i].info.image_bpc;
912                         case IL_BGR:
913                                 p[i].red_offset   = 2 * p[i].info.image_bpc;
914                                 p[i].green_offset = 1 * p[i].info.image_bpc;
915                                 p[i].blue_offset  = 0 * p[i].info.image_bpc;
916                                 break;
917                         case IL_RGBA:
918                                 p[i].flags |= HAS_ALPHA | EFF_ALPHA;
919                                 p[i].alpha_offset = 3 * p[i].info.image_bpc;
920                         case IL_RGB:
921                         default:
922                                 p[i].red_offset   = 0 * p[i].info.image_bpc;
923                                 p[i].green_offset = 1 * p[i].info.image_bpc;
924                                 p[i].blue_offset  = 2 * p[i].info.image_bpc;
925                                 break;
926                         }
927                         if (p[i].info.image_bpc > 1)
928                                 p[i].flags |= IS_OVER_8BIT;
929                         
930                         if ((p[i].flags & IS_INDEXED) && (p[i].flags & CANNOT_BE_INDEXED))
931                                 p[i].flags &= ~EFF_INDEXED;
932                         if ((p[i].flags & IS_GRAY) && (p[i].flags & CANNOT_BE_GRAY))
933                                 p[i].flags &= ~EFF_GRAY;
934                         else if (!(p[i].flags & IS_GRAY) && (p[i].flags & MUST_BE_GRAY))
935                                 p[i].flags |= EFF_GRAY;
936                         if ((!(p[i].flags & HAS_ALPHA)) && (p[i].flags & MUST_HAVE_ALPHA))
937                                 p[i].flags |= EFF_ALPHA;
938                         else if ((p[i].flags & HAS_ALPHA) && (p[i].flags & CANNOT_HAVE_ALPHA))
939                                 p[i].flags &= ~EFF_ALPHA;
940                 }
941                 if (!skip_frame)
942                 {
943                         if (palette_only)
944                         {
945                                 for (i=0; i<n; ++i)
946                                 {
947                                         p[i].index = 0;
948                                         p[i].pal_offset = 0;
949                                         flags[i] |= IS_PALETTE_ONLY;
950                                 }
951                                 for (j=0; (j<actual_colours) && (r==0); ++j)
952                                 {
953                                         for (i=0; i<n; ++i)
954                                         {
955                                                 if (!(p[i].flags & NOT_READABLE))
956                                                 {
957                                                         p[i].red   = (ILuint)(*((ILubyte*)(p[i].palette + p[i].pal_offset + p[i].red_offset)));
958                                                         p[i].green = (ILuint)(*((ILubyte*)(p[i].palette + p[i].pal_offset + p[i].green_offset)));
959                                                         p[i].blue  = (ILuint)(*((ILubyte*)(p[i].palette + p[i].pal_offset + p[i].blue_offset)));
960                                                         if (p[i].flags & HAS_ALPHA)
961                                                         {
962                                                                 if (p[i].flags & CANNOT_HAVE_ALPHA)
963                                                                         p[i].alpha = 0xFF;
964                                                                 else
965                                                                         p[i].alpha = (ILuint)(*((ILubyte*)(p[i].palette + p[i].pal_offset + p[i].alpha_offset)));
966                                                         }
967                                                         else if (p[i].flags & MUST_HAVE_ALPHA)
968                                                                 p[i].alpha = 0xFF;
969                                                         if (p[i].flags & MUST_BE_GRAY)
970                                                                 p[i].value = (p[i].red + p[i].green + p[i].blue) / 3;
971                                                 }
972                                         }
973                                         
974                                         r = function(n, p, data);
975                                         if (r)
976                                                 break;
977                                         
978                                         for (i=0; i<n; ++i)
979                                         {
980                                                 if (!(p[i].flags & NOT_WRITABLE))
981                                                 {
982                                                         if (p[i].flags & MUST_BE_GRAY)
983                                                         {
984                                                                 p[i].red   = p[i].value;
985                                                                 p[i].green = p[i].value;
986                                                                 p[i].blue  = p[i].value;
987                                                         }
988                                                         if (p[i].flags & HAS_ALPHA)
989                                                         {
990                                                                 if (p[i].flags & CANNOT_HAVE_ALPHA)
991                                                                         p[i].alpha = 0xFF;
992                                                                 *((ILubyte*)(p[i].palette + p[i].pal_offset + p[i].alpha_offset)) = (ILubyte)(p[i].alpha);
993                                                         }
994                                                         *((ILubyte*)(p[i].palette + p[i].pal_offset + p[i].red_offset))   = (ILubyte)(p[i].red);
995                                                         *((ILubyte*)(p[i].palette + p[i].pal_offset + p[i].green_offset)) = (ILubyte)(p[i].green);
996                                                         *((ILubyte*)(p[i].palette + p[i].pal_offset + p[i].blue_offset))  = (ILubyte)(p[i].blue);
997                                                 }
998                                                 
999                                                 ++(p[i].index);
1000                                                 p[i].pal_offset += p[i].info.palette_bpp;
1001                                         }
1002                                 }
1003                         }
1004                         else
1005                         {
1006                                 for (i=0; i<n; ++i)
1007                                 {
1008                                         p[i].line_bytes = p[i].info.image_bytes_per_pixel * p[i].info.image_width;
1009                                         p[i].frame_bytes = p[i].line_bytes * p[i].info.image_height;
1010                                         
1011                                         if (p[i].info.image_origin == IL_ORIGIN_LOWER_LEFT)
1012                                         {
1013                                                 p[i].line_start = p[i].frame_bytes - p[i].line_bytes;
1014                                                 p[i].line_bytes = 0 - p[i].line_bytes;
1015                                         }
1016                                         else
1017                                                 p[i].line_start = 0;
1018                                         
1019                                         p[i].y_window = 0;
1020                                         p[i].y_pict = p[i].y0;
1021                                         p[i].line_offset = p[i].line_start + (p[i].y_pict * p[i].line_bytes);
1022                                 }
1023                                 for (y=0; (y<height)  && (r == 0); ++y)
1024                                 {
1025                                         skip_line = 0;
1026                                         for (i=0; i<n; ++i)
1027                                         {
1028                                                 if ((p[i].y_pict < 0) || (p[i].y_pict >= p[i].info.image_height))
1029                                                 {
1030                                                         skip_line = 1;
1031                                                         break;
1032                                                 }
1033                                                 
1034                                                 p[i].x_window = 0;
1035                                                 p[i].x_pict = p[i].x0;
1036                                                 p[i].pixel_offset = p[i].line_offset + (p[i].x_pict * p[i].info.image_bytes_per_pixel);
1037                                         }
1038                                         if (!skip_line)
1039                                         {
1040                                                 for (x=0; x<width; ++x)
1041                                                 {
1042                                                         skip_pixel = 0;
1043                                                         for (i=0; i<n; ++i)
1044                                                         {
1045                                                                 if ((p[i].x_pict < 0) || (p[i].x_pict >= p[i].info.image_width))
1046                                                                 {
1047                                                                         skip_pixel = 1;
1048                                                                         break;
1049                                                                 }
1050                                                         }
1051                                                         if (!skip_pixel)
1052                                                         {
1053                                                                 for (i=0; i<n; ++i)
1054                                                                 {
1055                                                                         if (!(p[i].flags & NOT_READABLE))
1056                                                                         {
1057                                                                                 /* can this be done better? think about it */
1058                                                                                 switch (p[i].info.image_type)
1059                                                                                 {
1060                                                                                 case IL_INT:
1061                                                                                 case IL_UNSIGNED_INT:
1062                                                                                         if (p[i].flags & IS_INDEXED)
1063                                                                                                 p[i].index = *((ILuint*)(p[i].data + p[i].pixel_offset + p[i].index_offset));
1064                                                                                         else
1065                                                                                         {
1066                                                                                                 if (p[i].flags & IS_GRAY)
1067                                                                                                         p[i].value = *((ILuint*)(p[i].data + p[i].pixel_offset + p[i].value_offset));
1068                                                                                                 else
1069                                                                                                 {
1070                                                                                                         p[i].red   = *((ILuint*)(p[i].data + p[i].pixel_offset + p[i].red_offset));
1071                                                                                                         p[i].green = *((ILuint*)(p[i].data + p[i].pixel_offset + p[i].green_offset));
1072                                                                                                         p[i].blue  = *((ILuint*)(p[i].data + p[i].pixel_offset + p[i].blue_offset));
1073                                                                                                 }
1074                                                                                                 if (p[i].flags & HAS_ALPHA)
1075                                                                                                         p[i].alpha = *((ILuint*)(p[i].data + p[i].pixel_offset + p[i].alpha_offset));
1076                                                                                         }
1077                                                                                         break;
1078                                                                                 case IL_SHORT:
1079                                                                                 case IL_UNSIGNED_SHORT:
1080                                                                                         if (p[i].flags & IS_INDEXED)
1081                                                                                                 p[i].index = (ILuint)(*((ILushort*)(p[i].data + p[i].pixel_offset + p[i].index_offset)));
1082                                                                                         else
1083                                                                                         {
1084                                                                                                 if(p[i].flags & IS_GRAY)
1085                                                                                                         p[i].value = (ILuint)(*((ILushort*)(p[i].data + p[i].pixel_offset + p[i].value_offset)));
1086                                                                                                 else
1087                                                                                                 {
1088                                                                                                         p[i].red   = (ILuint)(*((ILushort*)(p[i].data + p[i].pixel_offset + p[i].red_offset)));
1089                                                                                                         p[i].green = (ILuint)(*((ILushort*)(p[i].data + p[i].pixel_offset + p[i].green_offset)));
1090                                                                                                         p[i].blue  = (ILuint)(*((ILushort*)(p[i].data + p[i].pixel_offset + p[i].blue_offset)));
1091                                                                                                 }
1092                                                                                                 if(p[i].flags & HAS_ALPHA)
1093                                                                                                         p[i].alpha = (ILuint)(*((ILushort*)(p[i].data + p[i].pixel_offset + p[i].alpha_offset)));
1094                                                                                         }
1095                                                                                         break;
1096                                                                                 case IL_BYTE:
1097                                                                                 case IL_UNSIGNED_BYTE:
1098                                                                                         if(p[i].flags & IS_INDEXED)
1099                                                                                                 p[i].index = (ILuint)(*((ILubyte*)(p[i].data + p[i].pixel_offset + p[i].index_offset)));
1100                                                                                         else
1101                                                                                         {
1102                                                                                                 if(p[i].flags & IS_GRAY)
1103                                                                                                         p[i].value = (ILuint)(*((ILubyte*)(p[i].data + p[i].pixel_offset + p[i].value_offset)));
1104                                                                                                 else
1105                                                                                                 {
1106                                                                                                         p[i].red   = (ILuint)(*((ILubyte*)(p[i].data + p[i].pixel_offset + p[i].red_offset)));
1107                                                                                                         p[i].green = (ILuint)(*((ILubyte*)(p[i].data + p[i].pixel_offset + p[i].green_offset)));
1108                                                                                                         p[i].blue  = (ILuint)(*((ILubyte*)(p[i].data + p[i].pixel_offset + p[i].blue_offset)));
1109                                                                                                 }
1110                                                                                                 if(p[i].flags & HAS_ALPHA)
1111                                                                                                         p[i].alpha = (ILuint)(*((ILubyte*)(p[i].data + p[i].pixel_offset + p[i].alpha_offset)));
1112                                                                                         }
1113                                                                                         break;
1114                                                                                 default:
1115                                                                                         break;
1116                                                                                 }
1117                                                                         
1118                                                                                 if ((p[i].flags & IS_INDEXED) && (p[i].flags & CANNOT_BE_INDEXED))
1119                                                                                 {
1120                                                                                         if (p[i].index < p[i].info.palette_num_cols)
1121                                                                                         {
1122                                                                                                 p[i].pal_offset = p[i].index * p[i].info.palette_bpp;
1123                                                                                                 p[i].red   = (ILuint)(*((ILubyte*)(p[i].palette + p[i].pal_offset + p[i].red_offset)));
1124                                                                                                 p[i].green = (ILuint)(*((ILubyte*)(p[i].palette + p[i].pal_offset + p[i].green_offset)));
1125                                                                                                 p[i].blue  = (ILuint)(*((ILubyte*)(p[i].palette + p[i].pal_offset + p[i].blue_offset)));
1126                                                                                                 if (p[i].flags & HAS_ALPHA)
1127                                                                                                         p[i].alpha = (ILuint)(*((ILubyte*)(p[i].palette + p[i].pal_offset + p[i].alpha_offset)));
1128                                                                                         }
1129                                                                                 }
1130                                                                                 
1131                                                                                 if ((p[i].flags & IS_GRAY) && (p[i].flags & CANNOT_BE_GRAY))
1132                                                                                 {
1133                                                                                         p[i].red   = p[i].value;
1134                                                                                         p[i].green = p[i].value;
1135                                                                                         p[i].blue  = p[i].value;
1136                                                                                 }
1137                                                                                 else if (!(p[i].flags & IS_GRAY) && (p[i].flags & MUST_BE_GRAY))
1138                                                                                         p[i].value = (p[i].red + p[i].green + p[i].blue) / 3;
1139                                                                                 
1140                                                                                 if (((!(p[i].flags & HAS_ALPHA)) && (p[i].flags & MUST_HAVE_ALPHA)) || ((p[i].flags & HAS_ALPHA) && (p[i].flags & CANNOT_HAVE_ALPHA)))
1141                                                                                         p[i].alpha = upscale_value(0xFF, p[i].info.image_bpc);
1142                                                                         }
1143                                                                 }
1144                                                                 
1145                                                                 r = function(n, p, data);
1146                                                                 if (r)
1147                                                                         break;
1148                                                                 
1149                                                                 for (i=0; i<n; ++i)
1150                                                                 {
1151                                                                         if (!(p[i].flags & NOT_WRITABLE))
1152                                                                         {
1153                                                                                 if ((p[i].flags & HAS_ALPHA) && (p[i].flags & CANNOT_HAVE_ALPHA))
1154                                                                                         p[i].alpha = upscale_value(0xFF, p[i].info.image_bpc);
1155                                                                                 
1156                                                                                 else if (!(p[i].flags & IS_GRAY) && (p[i].flags & MUST_BE_GRAY))
1157                                                                                 {
1158                                                                                         p[i].red   = p[i].value;
1159                                                                                         p[i].green = p[i].value;
1160                                                                                         p[i].blue  = p[i].value;
1161                                                                                 }
1162                                                                                 else if ((p[i].flags & IS_GRAY) && (p[i].flags & CANNOT_BE_GRAY))
1163                                                                                         p[i].value = (p[i].red + p[i].green + p[i].blue) / 3;
1164                                                                         
1165                                                                                 switch (p[i].info.image_type)
1166                                                                                 {
1167                                                                                 case IL_INT:
1168                                                                                 case IL_UNSIGNED_INT:
1169                                                                                         if(p[i].flags & IS_INDEXED)
1170                                                                                                 *((ILuint*)(p[i].data + p[i].pixel_offset + p[i].index_offset)) = p[i].index;
1171                                                                                         else
1172                                                                                         {
1173                                                                                                 if(p[i].flags & IS_GRAY)
1174                                                                                                         *((ILuint*)(p[i].data + p[i].pixel_offset + p[i].value_offset)) = p[i].value;
1175                                                                                                 else
1176                                                                                                 {
1177                                                                                                         *((ILuint*)(p[i].data + p[i].pixel_offset + p[i].red_offset))   = p[i].red;
1178                                                                                                         *((ILuint*)(p[i].data + p[i].pixel_offset + p[i].green_offset)) = p[i].green;
1179                                                                                                         *((ILuint*)(p[i].data + p[i].pixel_offset + p[i].blue_offset))  = p[i].blue;
1180                                                                                                 }
1181                                                                                                 if(p[i].flags & HAS_ALPHA)
1182                                                                                                         *((ILuint*)(p[i].data + p[i].pixel_offset + p[i].alpha_offset)) = (ILubyte)p[i].alpha;
1183                                                                                         }
1184                                                                                         break;
1185                                                                                 case IL_SHORT:
1186                                                                                 case IL_UNSIGNED_SHORT:
1187                                                                                         if(p[i].flags & IS_INDEXED)
1188                                                                                                 *((ILushort*)(p[i].data + p[i].pixel_offset + p[i].index_offset)) = (ILushort)p[i].index;
1189                                                                                         else
1190                                                                                         {
1191                                                                                                 if(p[i].flags & IS_GRAY)
1192                                                                                                         *((ILushort*)(p[i].data + p[i].pixel_offset + p[i].value_offset)) = (ILushort)p[i].value;
1193                                                                                                 else
1194                                                                                                 {
1195                                                                                                         *((ILushort*)(p[i].data + p[i].pixel_offset + p[i].red_offset))   = (ILushort)p[i].red;
1196                                                                                                         *((ILushort*)(p[i].data + p[i].pixel_offset + p[i].green_offset)) = (ILushort)p[i].green;
1197                                                                                                         *((ILushort*)(p[i].data + p[i].pixel_offset + p[i].blue_offset))  = (ILushort)p[i].blue;
1198                                                                                                 }
1199                                                                                                 if(p[i].flags & HAS_ALPHA)
1200                                                                                                         *((ILushort*)(p[i].data + p[i].pixel_offset + p[i].alpha_offset)) = (ILushort)p[i].alpha;
1201                                                                                         }
1202                                                                                         break;
1203                                                                                 case IL_BYTE:
1204                                                                                 case IL_UNSIGNED_BYTE:
1205                                                                                         if(p[i].flags & IS_INDEXED)
1206                                                                                                 *((ILubyte*)(p[i].data + p[i].pixel_offset + p[i].index_offset)) = (ILubyte)p[i].index;
1207                                                                                         else
1208                                                                                         {
1209                                                                                                 if(p[i].flags & IS_GRAY)
1210                                                                                                         *((ILubyte*)(p[i].data + p[i].pixel_offset + p[i].value_offset)) = (ILubyte)p[i].value;
1211                                                                                                 else
1212                                                                                                 {
1213                                                                                                         *((ILubyte*)(p[i].data + p[i].pixel_offset + p[i].red_offset))   = (ILubyte)p[i].red;
1214                                                                                                         *((ILubyte*)(p[i].data + p[i].pixel_offset + p[i].green_offset)) = (ILubyte)p[i].green;
1215                                                                                                         *((ILubyte*)(p[i].data + p[i].pixel_offset + p[i].blue_offset))  = (ILubyte)p[i].blue;
1216                                                                                                 }
1217                                                                                                 if(p[i].flags & HAS_ALPHA)
1218                                                                                                         *((ILubyte*)(p[i].data + p[i].pixel_offset + p[i].alpha_offset)) = (ILubyte)p[i].alpha;
1219                                                                                         }
1220                                                                                         break;
1221                                                                                 default:
1222                                                                                         break;
1223                                                                                 }
1224                                                                         }
1225                                                                 }
1226                                                         }
1227                                                         for (i=0; i<n; ++i)
1228                                                         {
1229                                                                 ++(p[i].x_window);
1230                                                                 ++(p[i].x_pict);
1231                                                                 p[i].pixel_offset += p[i].info.image_bytes_per_pixel;
1232                                                         }
1233                                                 }
1234                                         }
1235                                         for (i=0; i<n; ++i)
1236                                         {
1237                                                 ++(p[i].y_window);
1238                                                 ++(p[i].y_pict);
1239                                                 p[i].line_offset += p[i].line_bytes;
1240                                         }
1241                                 }
1242                         }
1243                 }
1244         }
1245         
1246         free(p);
1247         return r;
1248 }
1249
1250 int perform_action_palette_mix (
1251         uint_fast16_t *id,
1252         ACTION_F *function,
1253         FLAG_TYPE *flags,
1254         void *data
1255 )
1256 {
1257         uint_fast16_t i;
1258         int r = 0;
1259         struct PixelInfo p[3];
1260         ILuint n_colors;
1261         ILint actual_frames = 0x7FFFFFFF;
1262         ILint f;
1263         uint_fast8_t skip_frame;
1264         ILuint j, k;
1265         
1266         
1267         for (i=0; i<3; ++i)
1268         {
1269                 p[i].id = id[i];
1270                 p[i].handle = get_handle(p[i].id);
1271                 p[i].flags = flags[i];
1272                 r = get_info(p[i].id, &(p[i].info), 0);
1273                 if (r!=0)
1274                         return r;
1275                 get_flags(&(p[i].info), &(p[i].flags));
1276                 
1277                 if (!(p[i].flags & IS_INDEXED))
1278                 {
1279                         fputs(INDEXED_REQUIRED, stderr);
1280                         return EINVAL;
1281                 }
1282                 if (!(p[i].flags & OK_PALETTE_ONLY))
1283                 {
1284                         fputs(PALETTE_ONLY_REQUIRED, stderr);
1285                         return EINVAL;
1286                 }
1287                 if (p[i].flags & CAN_BE_MULTIPLE)
1288                 {
1289                         if (p[i].info.num_images+1 < actual_frames)
1290                                 actual_frames = p[i].info.num_images+1;
1291                 }
1292                 else
1293                         actual_frames = 1;
1294                 
1295                 p[i].f0 = 0;
1296         }
1297         
1298         n_colors = p[0].info.palette_num_cols * p[1].info.palette_num_cols;
1299         if (p[2].info.palette_num_cols < n_colors)
1300         {
1301                 fputs(BAD_PALETTE_SIZE, stderr);
1302                 return EINVAL;
1303         }
1304         
1305         for (f = 0; (f < actual_frames) && (r == 0); ++f)
1306         {
1307                 skip_frame = 0;
1308                 for (i=0; i<3; ++i)
1309                 {
1310                         p[i].f_window = f;
1311                         p[i].f_pict = p[i].f0 + f;
1312                         if ((p[i].f_pict < 0) || (p[i].f_pict > p[i].info.num_images))
1313                         {
1314                                 skip_frame = 1;
1315                                 break;
1316                         }
1317                         
1318                         r = get_info(p[i].id, &(p[i].info), p[i].f_pict);
1319                         if (r!=0)
1320                                 return r;
1321                         
1322                         get_flags(&(p[i].info), &(p[i].flags));
1323                         p[i].flags |= IS_PALETTE_ONLY;
1324                         
1325                         r = get_data(p[i].id, &(p[i].data),  p[i].f_pict);
1326                         if (r!=0)
1327                                 return r;
1328                         
1329                         r = get_palette(p[i].id, &(p[i].palette),  p[i].f_pict);
1330                         if (r!=0)
1331                                 return r;
1332                         
1333                         if (!(p[i].flags & IS_INDEXED))
1334                         {
1335                                 fputs(INDEXED_REQUIRED, stderr);
1336                                 return EINVAL;
1337                         }
1338                         
1339                         p[i].alpha_offset = 0;
1340                         p[i].value_offset = 0;
1341                         p[i].index_offset = 0;
1342                         p[i].red_offset = 0;
1343                         p[i].green_offset = 0;
1344                         p[i].blue_offset = 0;
1345                         
1346                         switch (p[i].info.palette_type)
1347                         {
1348                         case IL_PAL_BGR32:
1349                         case IL_PAL_RGB32:
1350                                 break;
1351                         case IL_PAL_BGRA32:
1352                                 p[i].alpha_offset = 3;
1353                         case IL_PAL_BGR24:
1354                                 p[i].red_offset = 2;
1355                                 p[i].green_offset = 1;
1356                                 p[i].blue_offset = 0;
1357                                 break;
1358                         case IL_PAL_RGBA32:
1359                                 p[i].alpha_offset = 3;
1360                         case IL_PAL_RGB24:
1361                         default:
1362                                 p[i].red_offset = 0;
1363                                 p[i].green_offset = 1;
1364                                 p[i].blue_offset = 2;
1365                                 break;
1366                         }
1367                         
1368                         if ((p[i].flags & IS_GRAY) && (p[i].flags & CANNOT_BE_GRAY))
1369                                 p[i].flags &= ~EFF_GRAY;
1370                         else if (!(p[i].flags & IS_GRAY) && (p[i].flags & MUST_BE_GRAY))
1371                                 p[i].flags |= EFF_GRAY;
1372                         if ((!(p[i].flags & HAS_ALPHA)) && (p[i].flags & MUST_HAVE_ALPHA))
1373                                 p[i].flags |= EFF_ALPHA;
1374                         else if ((p[i].flags & HAS_ALPHA) && (p[i].flags & CANNOT_HAVE_ALPHA))
1375                                 p[i].flags &= ~EFF_ALPHA;
1376                 }
1377                 if (!skip_frame)
1378                 {
1379                         p[0].index = 0;
1380                         p[0].pal_offset = 0;
1381                         p[2].index = 0;
1382                         p[2].pal_offset = 0;
1383                         for (j=0; j<p[0].info.palette_num_cols; ++j)
1384                         {
1385                                 p[1].index = 0;
1386                                 p[1].pal_offset = 0;
1387                                 for (k=0; k<p[1].info.palette_num_cols; ++k)
1388                                 {
1389                                         for (i=0; i<3; ++i)
1390                                         {
1391                                                 if (!(p[i].flags & NOT_READABLE))
1392                                                 {
1393                                                         p[i].red   = (ILuint)(*((ILubyte*)(p[i].palette + p[i].pal_offset + p[i].red_offset)));
1394                                                         p[i].green = (ILuint)(*((ILubyte*)(p[i].palette + p[i].pal_offset + p[i].green_offset)));
1395                                                         p[i].blue  = (ILuint)(*((ILubyte*)(p[i].palette + p[i].pal_offset + p[i].blue_offset)));
1396                                                         if (p[i].flags & HAS_ALPHA)
1397                                                         {
1398                                                                 if (p[i].flags & CANNOT_HAVE_ALPHA)
1399                                                                         p[i].alpha = 0xFF;
1400                                                                 else
1401                                                                         p[i].alpha = (ILuint)(*((ILubyte*)(p[i].palette + p[i].pal_offset + p[i].alpha_offset)));
1402                                                         }
1403                                                         else if (p[i].flags & MUST_HAVE_ALPHA)
1404                                                                 p[i].alpha = 0xFF;
1405                                                         if (p[i].flags & MUST_BE_GRAY)
1406                                                                 p[i].value = (p[i].red + p[i].green + p[i].blue) / 3;
1407                                                 }
1408                                         }
1409                                         
1410                                         r = function(3, p, data);
1411                                         if (r)
1412                                                 break;
1413                                         
1414                                         for (i=0; i<3; ++i)
1415                                         {
1416                                                 if (!(p[i].flags & NOT_WRITABLE))
1417                                                 {
1418                                                         if (p[i].flags & MUST_BE_GRAY)
1419                                                         {
1420                                                                 p[i].red   = p[i].value;
1421                                                                 p[i].green = p[i].value;
1422                                                                 p[i].blue  = p[i].value;
1423                                                         }
1424                                                         if (p[i].flags & HAS_ALPHA)
1425                                                         {
1426                                                                 if (p[i].flags & CANNOT_HAVE_ALPHA)
1427                                                                         p[i].alpha = 0xFF;
1428                                                                 *((ILubyte*)(p[i].palette + p[i].pal_offset + p[i].alpha_offset)) = (ILubyte)(p[i].alpha);
1429                                                         }
1430                                                         *((ILubyte*)(p[i].palette + p[i].pal_offset + p[i].red_offset))   = (ILubyte)(p[i].red);
1431                                                         *((ILubyte*)(p[i].palette + p[i].pal_offset + p[i].green_offset)) = (ILubyte)(p[i].green);
1432                                                         *((ILubyte*)(p[i].palette + p[i].pal_offset + p[i].blue_offset))  = (ILubyte)(p[i].blue);
1433                                                 }
1434                                         }
1435                                         
1436                                         ++p[1].index;
1437                                         p[1].pal_offset += p[1].info.palette_bpp;
1438                                         ++p[2].index;
1439                                         p[2].pal_offset += p[1].info.palette_bpp;
1440                                 }
1441                                 ++p[0].index;
1442                                 p[0].pal_offset += p[0].info.palette_bpp;
1443                         }
1444                 }
1445         }
1446         
1447         return r;
1448 }
1449
1450 ILuint upscale_value (ILubyte x, ILint bytes)
1451 {
1452         ILint i;
1453         ILuint y = 0;
1454         
1455         for (i=0; i<bytes; ++i)
1456         {
1457                 y <<= 8;
1458                 y |= x;
1459         }
1460         return y;
1461 }
1462
1463 ILubyte downscale_value (ILuint x, ILint bytes)
1464 {
1465         return (ILubyte)(x >> (8 * (bytes-1)));
1466 }
1467
1468 int copy_pixels (ILuint n, struct PixelInfo *p, void *data)
1469 {
1470         ILuint max = *((ILuint *)data);
1471         
1472         if (n < 2)
1473                 return EINVAL;
1474         
1475         if (p[0].flags & EFF_INDEXED)
1476         {
1477                 if (!(p[1].flags & EFF_INDEXED))
1478                         return EINVAL;
1479                 p[1].index = p[0].index;
1480         }
1481         else if (p[0].flags & EFF_ALPHA)
1482         {
1483                 if (p[0].flags & EFF_GRAY)
1484                 {
1485                         if (!(p[1].flags & EFF_GRAY))
1486                                 return EINVAL;
1487                         p[1].value = copy_1p_alpha(p[0].value, p[1].value, p[0].alpha, max);
1488                 }
1489                 else
1490                 {
1491                         if (p[1].flags & EFF_GRAY)
1492                                 return EINVAL;
1493                         p[1].red   = copy_1p_alpha(p[0].red,   p[1].red,   p[0].alpha, max);
1494                         p[1].green = copy_1p_alpha(p[0].green, p[1].green, p[0].alpha, max);
1495                         p[1].blue  = copy_1p_alpha(p[0].blue,  p[1].blue,  p[0].alpha, max);
1496                 }
1497                 if (p[1].flags & EFF_ALPHA)
1498                         p[1].alpha = copy_1p_alpha(max, p[1].alpha, p[0].alpha, max);
1499         }
1500         else
1501         {
1502                 if (p[0].flags & EFF_GRAY)
1503                 {
1504                         if (!(p[1].flags & EFF_GRAY))
1505                                 return EINVAL;
1506                         p[1].value = p[0].value;
1507                 }
1508                 else
1509                 {
1510                         if (p[1].flags & EFF_GRAY)
1511                                 return EINVAL;
1512                         p[1].red   = p[0].red;
1513                         p[1].green = p[0].green;
1514                         p[1].blue  = p[0].blue;
1515                 }
1516                 if (p[1].flags & EFF_ALPHA)
1517                         p[1].alpha = max;
1518         }
1519         
1520         return 0;
1521 }
1522
1523 int fill_color (ILuint n, struct PixelInfo *p, void *data)
1524 {
1525         struct ColorInfo *d = data;
1526         
1527         if (p->flags & EFF_INDEXED)
1528         {
1529                 p->index = d->index;
1530         }
1531         else 
1532         {
1533                 if (p->flags & EFF_ALPHA)
1534                         p->alpha = d->alpha;
1535                 if (p->flags & EFF_GRAY)
1536                         p->value = d->value;
1537                 else
1538                 {
1539                         p->red   = d->red;
1540                         p->green = d->green;
1541                         p->blue  = d->blue;
1542                 }
1543         }
1544         
1545         return 0;
1546 }
1547
1548 ILuint copy_1p_alpha (ILint64 src, ILint64 dst, ILint64 alpha, ILint64 max)
1549 {
1550         ILint64 v = (src * alpha + dst * (max - alpha)) / max;
1551         return (ILuint)v;
1552 }
1553
1554 int palette_mix_index (ILuint n, struct PixelInfo *p, void *data)
1555 {
1556         if (n < 3)
1557                 return EIO;
1558         
1559         p[2].index = p[0].index * p[1].info.palette_num_cols + p[1].index;
1560         
1561         return 0;
1562 }
1563
1564 ILuint tsqrt(ILuint s)
1565 {
1566         switch(s)
1567         {
1568         case 256:
1569                 return 16;
1570         case 225:
1571                 return 15;
1572         case 196:
1573                 return 14;
1574         case 169:
1575                 return 13;
1576         case 144:
1577                 return 12;
1578         case 121:
1579                 return 11;
1580         case 100:
1581                 return 10;
1582         case 81:
1583                 return 9;
1584         case 64:
1585                 return 8;
1586         case 49:
1587                 return 7;
1588         case 36:
1589                 return 6;
1590         case 25:
1591                 return 5;
1592         case 16:
1593                 return 4;
1594         case 9:
1595                 return 3;
1596         case 4:
1597                 return 2;
1598         case 1:
1599                 return 1;
1600         default: // not a square - please fail
1601                 return 0;
1602         }
1603 }
1604
1605 //unsigned short isqrt(unsigned s)
1606 //{
1607 //      unsigned short r;
1608 //      unsigned short b=0x0040;
1609 //      
1610 //      while(b>s)
1611 //            b>>=2;
1612 //      while(b)
1613 //      {
1614 //            if(s>=r+b)
1615 //            {
1616 //                    s-=r+b;
1617 //                    r=(r>>1)+b;
1618 //            }
1619 //            else
1620 //                    r>>1;
1621 //            b>>2;
1622 //      }
1623 //      return r;
1624 //}
1625
1626
1627 // int action(
1628         // ILuint n, struct PixelInfo *info, void *data
1629 // )
1630 // {
1631         // printf(
1632                 // "%04lu %04lu %04lu: %03lu %03lu %03lu %03lu  %03lu %03lu\n",
1633                 // (unsigned long)(info->x_pict), (unsigned long)(info->y_pict), (unsigned long)(info->f_pict),
1634                 // (unsigned long)(info->red), (unsigned long)(info->green), (unsigned long)(info->blue),
1635                 // (unsigned long)(info->alpha), (unsigned long)(info->value), (unsigned long)(info->index)
1636         // );
1637         // return 0;
1638 // }