rnd-20030208-2-src
[rocksndiamonds.git] / src / libgame / image.c
1 /***********************************************************
2 * Artsoft Retro-Game Library                               *
3 *----------------------------------------------------------*
4 * (c) 1994-2002 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * image.c                                                  *
12 ***********************************************************/
13
14 #include "image.h"
15 #include "pcx.h"
16 #include "misc.h"
17 #include "setup.h"
18
19
20 /* ========================================================================= */
21 /* PLATFORM SPECIFIC IMAGE FUNCTIONS                                         */
22 /* ========================================================================= */
23
24 #if defined(TARGET_X11)
25
26 /* for MS-DOS/Allegro, exclude all except newImage() and freeImage() */
27
28 Image *newImage(unsigned int width, unsigned int height, unsigned int depth)
29 {
30   Image *image;
31   unsigned int bytes_per_pixel = (depth + 7) / 8;
32   int i;
33
34 #if 0
35   if (depth > 8)
36     Error(ERR_EXIT, "images with more than 256 colors are not supported");
37
38   depth = 8;
39 #endif
40
41   image = checked_calloc(sizeof(Image));
42   image->data = checked_calloc(width * height * bytes_per_pixel);
43   image->width = width;
44   image->height = height;
45   image->depth = depth;
46   image->bytes_per_pixel = bytes_per_pixel;
47   image->bytes_per_row = width * bytes_per_pixel;
48
49   image->rgb.used = 0;
50   for (i=0; i<MAX_COLORS; i++)
51     image->rgb.color_used[i] = FALSE;
52
53   image->type = (depth < 8 ? IMAGETYPE_BITMAP :
54                  depth > 8 ? IMAGETYPE_TRUECOLOR : IMAGETYPE_RGB);
55
56   return image;
57 }
58
59 void freeImage(Image *image)
60 {
61   free(image->data);
62   free(image);
63 }
64
65 #if defined(PLATFORM_UNIX)
66
67 /* extra colors to try allocating in private color maps to minimize flashing */
68 #define NOFLASH_COLORS 256
69
70 /* architecture independent value <-> memory conversions;
71    note: the internal format is big endian */
72
73 #define memory_to_value(ptr, len) (                                         \
74 (len) == 1 ? (unsigned long)(                 *( (byte *)(ptr))         ) : \
75 (len) == 2 ? (unsigned long)(((unsigned long)(*( (byte *)(ptr))   ))<< 8)   \
76                           + (                 *(((byte *)(ptr))+1)      ) : \
77 (len) == 3 ? (unsigned long)(((unsigned long)(*( (byte *)(ptr))   ))<<16)   \
78                           + (((unsigned long)(*(((byte *)(ptr))+1)))<< 8)   \
79                           + (                 *(((byte *)(ptr))+2)      ) : \
80              (unsigned long)(((unsigned long)(*( (byte *)(ptr))   ))<<24)   \
81                           + (((unsigned long)(*(((byte *)(ptr))+1)))<<16)   \
82                           + (((unsigned long)(*(((byte *)(ptr))+2)))<< 8)   \
83                           + (                 *(((byte *)(ptr))+3)      ) )
84
85
86 #define value_to_memory(value, ptr, len) (                              \
87 (len) == 1 ? (*( (byte *)(ptr)   ) = ( value     ) ) :                  \
88 (len) == 2 ? (*( (byte *)(ptr)   ) = (((unsigned long)(value))>> 8),    \
89               *(((byte *)(ptr))+1) = ( value     ) ) :                  \
90 (len) == 3 ? (*( (byte *)(ptr)   ) = (((unsigned long)(value))>>16),    \
91               *(((byte *)(ptr))+1) = (((unsigned long)(value))>> 8),    \
92               *(((byte *)(ptr))+2) = ( value     ) ) :                  \
93              (*( (byte *)(ptr)   ) = (((unsigned long)(value))>>24),    \
94               *(((byte *)(ptr))+1) = (((unsigned long)(value))>>16),    \
95               *(((byte *)(ptr))+2) = (((unsigned long)(value))>> 8),    \
96               *(((byte *)(ptr))+3) = ( value     ) ))
97
98 static Pixmap Image_to_Mask(Image *image, Display *display, Window window)
99 {
100   byte *src_ptr, *dst_ptr, *dst_ptr2;
101   unsigned int bytes_per_row;
102   unsigned int x, y, i;
103   byte bitmask;
104   byte *mask_data;
105   Pixmap mask_pixmap;
106
107   bytes_per_row = (image->width + 7) / 8;
108   mask_data = checked_calloc(bytes_per_row * image->height);
109
110   src_ptr = image->data;
111   dst_ptr = mask_data;
112
113   /* create bitmap data which can be used by 'XCreateBitmapFromData()'
114    * directly to create a pixmap of depth 1 for use as a clip mask for
115    * the corresponding image pixmap
116    */
117
118   for (y=0; y<image->height; y++)
119   {
120     bitmask = 0x01;             /* start with leftmost bit in the byte     */
121     dst_ptr2 = dst_ptr;         /* start with leftmost byte in the row     */
122
123     for (x=0; x<image->width; x++)
124     {
125       for (i=0; i<image->bytes_per_pixel; i++)
126         if (*src_ptr++)         /* source pixel solid? (pixel index != 0)  */
127           *dst_ptr2 |= bitmask; /* then write a bit into the image mask    */
128
129       if ((bitmask <<= 1) == 0) /* bit at rightmost byte position reached? */
130       {
131         bitmask = 0x01;         /* start again with leftmost bit position  */
132         dst_ptr2++;             /* continue with next byte in image mask   */
133       }
134     }
135
136     dst_ptr += bytes_per_row;   /* continue with leftmost byte of next row */
137   }
138
139   mask_pixmap = XCreateBitmapFromData(display, window, (char *)mask_data,
140                                       image->width, image->height);
141   free(mask_data);
142
143   return mask_pixmap;
144 }
145
146 static int bitsPerPixelAtDepth(Display *display, int screen, int depth)
147 {
148   XPixmapFormatValues *pixmap_format;
149   int i, num_pixmap_formats, bits_per_pixel = -1;
150
151   /* get Pixmap formats supported by the X server */
152   pixmap_format = XListPixmapFormats(display, &num_pixmap_formats);
153
154   /* find format that matches the given depth */
155   for (i=0; i<num_pixmap_formats; i++)
156     if (pixmap_format[i].depth == depth)
157       bits_per_pixel = pixmap_format[i].bits_per_pixel;
158
159   XFree(pixmap_format);
160
161   if (bits_per_pixel == -1)
162     Error(ERR_EXIT, "cannot find pixmap format for depth %d", depth);
163
164   return bits_per_pixel;
165 }
166
167 XImageInfo *Image_to_Pixmap(Display *display, int screen, Visual *visual,
168                             Window window, GC gc, int depth, Image *image)
169 {
170   static XColor xcolor_private[NOFLASH_COLORS];
171   static int colorcell_used[NOFLASH_COLORS];
172   static Colormap global_cmap = 0;
173   static Pixel *global_cmap_index;
174   static int num_cmap_entries, free_cmap_entries;
175   static boolean private_cmap = FALSE;
176   Pixel *redvalue, *greenvalue, *bluevalue;
177   unsigned int display_bytes_per_pixel, display_bits_per_pixel;
178   unsigned int a, c = 0, x, y;
179   XColor xcolor;
180   XImage *ximage;
181   XImageInfo *ximageinfo;
182   byte *src_ptr, *dst_ptr;
183   char *error = "Image_to_Pixmap(): %s";
184
185   if (image->type == IMAGETYPE_TRUECOLOR && depth == 8)
186   {
187     SetError(error, "cannot handle true-color images on 8-bit display");
188     return NULL;
189   }
190
191   if (!global_cmap)
192   {
193     if (visual == DefaultVisual(display, screen))
194       global_cmap = DefaultColormap(display, screen);
195     else
196     {
197       global_cmap = XCreateColormap(display, RootWindow(display, screen),
198                                     visual, AllocNone);
199       private_cmap = TRUE;
200     }
201   }
202
203   xcolor.flags = DoRed | DoGreen | DoBlue;
204   redvalue = greenvalue = bluevalue = NULL;
205   ximageinfo = checked_malloc(sizeof(XImageInfo));
206   ximageinfo->display = display;
207   ximageinfo->depth = depth;
208
209   switch (visual->class)
210   {
211     case TrueColor:
212     case DirectColor:
213     {
214       Pixel pixval;
215       unsigned int redcolors, greencolors, bluecolors;
216       unsigned int redstep, greenstep, bluestep;
217       unsigned int redbottom, greenbottom, bluebottom;
218       unsigned int redtop, greentop, bluetop;
219
220       redvalue = (Pixel *)checked_malloc(sizeof(Pixel) * 256);
221       greenvalue = (Pixel *)checked_malloc(sizeof(Pixel) * 256);
222       bluevalue = (Pixel *)checked_malloc(sizeof(Pixel) * 256);
223
224       ximageinfo->cmap = global_cmap;
225
226       retry_direct: /* tag we hit if a DirectColor allocation fails on
227                      * default colormap */
228
229       /* calculate number of distinct colors in each band */
230
231       redcolors = greencolors = bluecolors = 1;
232       for (pixval=1; pixval; pixval <<= 1)
233       {
234         if (pixval & visual->red_mask)
235           redcolors <<= 1;
236         if (pixval & visual->green_mask)
237           greencolors <<= 1;
238         if (pixval & visual->blue_mask)
239           bluecolors <<= 1;
240       }
241       
242       /* consistency check */
243       if (redcolors > visual->map_entries ||
244           greencolors > visual->map_entries ||
245           bluecolors > visual->map_entries)
246         Error(ERR_WARN, "inconsistency in color information");
247
248       redstep = 256 / redcolors;
249       greenstep = 256 / greencolors;
250       bluestep = 256 / bluecolors;
251       redbottom = greenbottom = bluebottom = 0;
252       redtop = greentop = bluetop = 0;
253
254       for (a=0; a<visual->map_entries; a++)
255       {
256         if (redbottom < 256)
257           redtop = redbottom + redstep;
258         if (greenbottom < 256)
259           greentop = greenbottom + greenstep;
260         if (bluebottom < 256)
261           bluetop = bluebottom + bluestep;
262
263         xcolor.red = (redtop - 1) << 8;
264         xcolor.green = (greentop - 1) << 8;
265         xcolor.blue = (bluetop - 1) << 8;
266         if (!XAllocColor(display, ximageinfo->cmap, &xcolor))
267         {
268           /* if an allocation fails for a DirectColor default visual then
269              we should create a private colormap and try again. */
270
271           if ((visual->class == DirectColor) &&
272               (visual == DefaultVisual(display, screen)))
273           {
274             global_cmap = XCopyColormapAndFree(display, global_cmap);
275             ximageinfo->cmap = global_cmap;
276             private_cmap = TRUE;
277
278             goto retry_direct;
279           }
280
281           /* something completely unexpected happened */
282
283           fprintf(stderr, "Image_to_Pixmap: XAllocColor failed on a TrueColor/Directcolor visual\n");
284
285           free(redvalue);
286           free(greenvalue);
287           free(bluevalue);
288           free(ximageinfo);
289
290           return NULL;
291         }
292
293         /* fill in pixel values for each band at this intensity */
294
295         while ((redbottom < 256) && (redbottom < redtop))
296           redvalue[redbottom++] = xcolor.pixel & visual->red_mask;
297         while ((greenbottom < 256) && (greenbottom < greentop))
298           greenvalue[greenbottom++] = xcolor.pixel & visual->green_mask;
299         while ((bluebottom < 256) && (bluebottom < bluetop))
300           bluevalue[bluebottom++] = xcolor.pixel & visual->blue_mask;
301       }
302
303       break;
304     }
305
306     case PseudoColor:
307
308       ximageinfo->cmap = global_cmap;
309
310       for (a=0; a<MAX_COLORS; a++)
311       {
312         XColor xcolor2;
313         unsigned short mask;
314         int color_found;
315         int i;
316
317         if (!image->rgb.color_used[a])
318           continue;
319
320         xcolor.red = *(image->rgb.red + a);
321         xcolor.green = *(image->rgb.green + a);
322         xcolor.blue = *(image->rgb.blue + a);
323   
324         /* look if this color already exists in our colormap */
325         if (!XAllocColor(display, ximageinfo->cmap, &xcolor))
326         {
327           if (!private_cmap)
328           {
329             if (options.verbose)
330               Error(ERR_RETURN, "switching to private colormap");
331
332             /* we just filled up the default colormap -- get a private one
333                which contains all already allocated colors */
334
335             global_cmap = XCopyColormapAndFree(display, global_cmap);
336             ximageinfo->cmap = global_cmap;
337             private_cmap = TRUE;
338
339             /* allocate the rest of the color cells read/write */
340             global_cmap_index =
341               (Pixel *)checked_malloc(sizeof(Pixel) * NOFLASH_COLORS);
342             for (i=0; i<NOFLASH_COLORS; i++)
343               if (!XAllocColorCells(display, global_cmap, FALSE, NULL, 0,
344                                     global_cmap_index + i, 1))
345                 break;
346             num_cmap_entries = free_cmap_entries = i;
347
348             /*
349             printf("We've got %d free colormap entries.\n", free_cmap_entries);
350             */
351
352             /* to minimize colormap flashing, copy default colors and try
353                to keep them as near as possible to the old values */
354
355             for(i=0; i<num_cmap_entries; i++)
356             {
357               xcolor2.pixel = *(global_cmap_index + i);
358               XQueryColor(display, DefaultColormap(display, screen), &xcolor2);
359               XStoreColor(display, global_cmap, &xcolor2);
360               xcolor_private[xcolor2.pixel] = xcolor2;
361               colorcell_used[xcolor2.pixel] = FALSE;
362             }
363
364             /* now we have the default colormap private: all colors we
365                successfully allocated so far are read-only, which is okay,
366                because we don't want to change them anymore -- if we need
367                an existing color again, we get it by XAllocColor; all other
368                colors are read/write and we can set them by XStoreColor,
369                but we will try to overwrite those color cells with our new
370                color which are as close as possible to our new color */
371           }
372
373           /* look for an existing default color close the one we want */
374
375           mask = 0xf000;
376           color_found = FALSE;
377
378           while (!color_found)
379           {
380             for (i=num_cmap_entries-1; i>=0; i--)
381             {
382               xcolor2.pixel = *(global_cmap_index + i);
383               xcolor2 = xcolor_private[xcolor2.pixel];
384
385               if (colorcell_used[xcolor2.pixel])
386                 continue;
387
388               if ((xcolor.red & mask) == (xcolor2.red & mask) &&
389                   (xcolor.green & mask) == (xcolor2.green & mask) &&
390                   (xcolor.blue & mask) == (xcolor2.blue & mask))
391               {
392                 /*
393                 printf("replacing color cell %ld with a close color\n",
394                        xcolor2.pixel);
395                        */
396                 color_found = TRUE;
397                 break;
398               }
399             }
400
401             if (mask == 0x0000)
402               break;
403
404             mask = (mask << 1) & 0xffff;
405           }
406
407           if (!color_found)             /* no more free color cells */
408           {
409             SetError(error, "cannot allocate enough color cells");
410             return NULL;
411           }
412
413           xcolor.pixel = xcolor2.pixel;
414           xcolor_private[xcolor.pixel] = xcolor;
415           colorcell_used[xcolor.pixel] = TRUE;
416           XStoreColor(display, ximageinfo->cmap, &xcolor);
417           free_cmap_entries--;
418         }
419
420         *(ximageinfo->index + a) = xcolor.pixel;
421       }
422
423       /*
424       printf("still %d free colormap entries\n", free_cmap_entries);
425       */
426
427       ximageinfo->no = a;       /* number of pixels allocated for this image */
428       break;
429   
430     default:
431       Error(ERR_RETURN,"DirectColor, TrueColor or PseudoColor display needed");
432       SetError(error, "display class not supported");
433
434       return NULL;
435   }
436
437 #if DEBUG_TIMING
438   debug_print_timestamp(2, "   ALLOCATING IMAGE COLORS:   ");
439 #endif
440
441   /* create XImage from internal image structure and convert it to Pixmap */
442
443   display_bits_per_pixel = bitsPerPixelAtDepth(display, screen, depth);
444   display_bytes_per_pixel = (display_bits_per_pixel + 7) / 8;
445
446   ximage = XCreateImage(display, visual, depth, ZPixmap,
447                         0, NULL, image->width, image->height,
448                         8, image->width * display_bytes_per_pixel);
449   ximage->data =
450     checked_malloc(image->width * image->height * display_bytes_per_pixel);
451   ximage->byte_order = MSBFirst;
452
453   src_ptr = image->data;
454   dst_ptr = (byte *)ximage->data;
455
456   switch (visual->class)
457   {
458     case DirectColor:
459     case TrueColor:
460     {
461       Pixel pixval;
462
463       switch (image->type)
464       {
465         case IMAGETYPE_RGB:
466         {
467           for (y=0; y<image->height; y++)               /* general case */
468           {
469             for (x=0; x<image->width; x++)
470             {
471               pixval = *src_ptr++;
472               pixval =
473                 redvalue[image->rgb.red[pixval] >> 8] |
474                 greenvalue[image->rgb.green[pixval] >> 8] |
475                 bluevalue[image->rgb.blue[pixval] >> 8];
476               value_to_memory(pixval, dst_ptr, display_bytes_per_pixel);
477               dst_ptr += display_bytes_per_pixel;
478             }
479           }
480
481           break;
482         }
483
484         case IMAGETYPE_TRUECOLOR:
485         {
486           for (y=0; y<image->height; y++)               /* general case */
487           {
488             for (x=0; x<image->width; x++)
489             {
490               pixval = memory_to_value(src_ptr, image->bytes_per_pixel);
491               pixval =
492                 redvalue[TRUECOLOR_RED(pixval)] |
493                 greenvalue[TRUECOLOR_GREEN(pixval)] |
494                 bluevalue[TRUECOLOR_BLUE(pixval)];
495               value_to_memory(pixval, dst_ptr, display_bytes_per_pixel);
496               src_ptr += image->bytes_per_pixel;
497               dst_ptr += display_bytes_per_pixel;
498             }
499           }
500
501           break;
502         }
503
504         default:
505           Error(ERR_RETURN, "RGB or TrueColor image needed");
506           SetError(error, "image type not supported");
507
508           return NULL;
509       }
510
511       break;
512     }
513
514     case PseudoColor:
515     {
516       if (display_bytes_per_pixel == 1)         /* special case */
517       {
518         for (y=0; y<image->height; y++)
519           for (x=0; x<image->width; x++)
520             *dst_ptr++ = ximageinfo->index[c + *src_ptr++];
521       }
522       else                                      /* general case */
523       {
524         for (y=0; y<image->height; y++)
525         {
526           for (x=0; x<image->width; x++)
527           {
528             value_to_memory(ximageinfo->index[c + *src_ptr++],
529                             dst_ptr, display_bytes_per_pixel);
530             dst_ptr += display_bytes_per_pixel;
531           }
532         }
533       }
534
535       break;
536     }
537
538     default:
539       Error(ERR_RETURN,"DirectColor, TrueColor or PseudoColor display needed");
540       SetError(error, "display class not supported");
541
542       return NULL;
543   }
544
545   if (redvalue)
546   {
547     free((byte *)redvalue);
548     free((byte *)greenvalue);
549     free((byte *)bluevalue);
550   }
551
552 #if DEBUG_TIMING
553   debug_print_timestamp(2, "   CONVERTING IMAGE TO XIMAGE:");
554 #endif
555
556   ximageinfo->pixmap = XCreatePixmap(display, window,
557                                      ximage->width, ximage->height,
558                                      ximageinfo->depth);
559
560   XPutImage(ximageinfo->display, ximageinfo->pixmap, gc,
561             ximage, 0, 0, 0, 0, ximage->width, ximage->height);
562
563   XDestroyImage(ximage);
564
565   return ximageinfo;
566 }
567
568 /*
569   -----------------------------------------------------------------------------
570   ZoomPixmap
571
572   Important note: The scaling code currently only supports scaling down the
573   image by a power of 2 -- scaling up is currently not supported at all!
574   -----------------------------------------------------------------------------
575 */
576
577 void ZoomPixmap(Display *display, GC gc, Pixmap src_pixmap, Pixmap dst_pixmap,
578                 int src_width, int src_height,
579                 int dst_width, int dst_height)
580 {
581   XImage *src_ximage, *dst_ximage;
582   byte *src_ptr, *dst_ptr;
583   int bits_per_pixel;
584   int bytes_per_pixel;
585   int x, y, i;
586   int zoom_factor = src_width / dst_width;      /* currently very limited! */
587   int row_skip, col_skip;
588
589   /* copy source pixmap to temporary image */
590   src_ximage = XGetImage(display, src_pixmap, 0, 0,
591                          src_width, src_height, AllPlanes, ZPixmap);
592
593   bits_per_pixel = src_ximage->bits_per_pixel;
594   bytes_per_pixel = (bits_per_pixel + 7) / 8;
595
596   dst_ximage = XCreateImage(display, visual, src_ximage->depth, ZPixmap,
597                             0, NULL, dst_width, dst_height,
598                             8, dst_width * bytes_per_pixel);
599   dst_ximage->data =
600     checked_malloc(dst_width * dst_height * bytes_per_pixel);
601   dst_ximage->byte_order = src_ximage->byte_order;
602
603   src_ptr = (byte *)src_ximage->data;
604   dst_ptr = (byte *)dst_ximage->data;
605
606   col_skip = (zoom_factor - 1) * bytes_per_pixel;
607   row_skip = col_skip * src_width;
608
609   /* scale image down by scaling factor 'zoom_factor' */
610   for (y=0; y < src_height; y += zoom_factor, src_ptr += row_skip)
611     for (x=0; x < src_width; x += zoom_factor, src_ptr += col_skip)
612       for (i=0; i<bytes_per_pixel; i++)
613         *dst_ptr++ = *src_ptr++;
614
615   /* copy scaled image to destination pixmap */
616   XPutImage(display, dst_pixmap, gc, dst_ximage, 0, 0, 0, 0,
617             MIN(src_width, dst_width), MIN(src_height, dst_height));
618
619   /* free temporary images */
620   XDestroyImage(src_ximage);
621   XDestroyImage(dst_ximage);
622 }
623
624 void freeXImage(Image *image, XImageInfo *ximageinfo)
625 {
626   if (ximageinfo->index != NULL && ximageinfo->no > 0)
627     XFreeColors(ximageinfo->display, ximageinfo->cmap, ximageinfo->index,
628                 ximageinfo->no, 0);
629   /* this       ^^^^^^^^^^^^^^ is wrong, because the used color cells
630    * are somewhere between 0 and MAX_COLORS; there are indeed 'ximageinfo->no'
631    * used color cells, but they are not at array position 0 - 'ximageinfo->no'
632    */
633
634   free(ximageinfo);
635 }
636
637 int Read_PCX_to_Pixmap(Display *display, Window window, GC gc, char *filename,
638                        Pixmap *pixmap, Pixmap *pixmap_mask)
639 {
640   Image *image;
641   XImageInfo *ximageinfo;
642   int screen;
643   Visual *visual;
644   int depth;
645
646 #if DEBUG_TIMING
647   debug_print_timestamp(2, NULL);       /* initialize timestamp function */
648 #endif
649
650   /* read the graphic file in PCX format to image structure */
651   if ((image = Read_PCX_to_Image(filename)) == NULL)
652     return errno_pcx;
653
654 #if DEBUG_TIMING
655   printf("%s:\n", filename);
656   debug_print_timestamp(2, "   READING PCX FILE TO IMAGE: ");
657 #endif
658
659   screen = DefaultScreen(display);
660   visual = DefaultVisual(display, screen);
661   depth = DefaultDepth(display, screen);
662
663   /* convert image structure to X11 Pixmap */
664   if (!(ximageinfo = Image_to_Pixmap(display, screen, visual,
665                                      window, gc, depth, image)))
666   {
667     freeImage(image);
668
669     return PCX_OtherError;
670   }
671
672   /* if a private colormap has been created, install it */
673   if (ximageinfo->cmap != DefaultColormap(display, screen))
674     XSetWindowColormap(display, window, ximageinfo->cmap);
675
676 #if DEBUG_TIMING
677   debug_print_timestamp(2, "   CONVERTING IMAGE TO PIXMAP:");
678 #endif
679
680   /* create clip mask for the image */
681   ximageinfo->pixmap_mask = Image_to_Mask(image, display, window);
682
683 #if DEBUG_TIMING
684   debug_print_timestamp(2, "   CONVERTING IMAGE TO MASK:  ");
685 #endif
686
687   *pixmap = ximageinfo->pixmap;
688   *pixmap_mask = ximageinfo->pixmap_mask;
689
690   /* free generic image and ximageinfo after native Pixmap has been created */
691   free(ximageinfo);
692   freeImage(image);
693
694   return PCX_Success;
695 }
696
697 #endif  /* PLATFORM_UNIX */
698 #endif  /* TARGET_X11 */
699
700
701 /* ========================================================================= */
702 /* PLATFORM INDEPENDANT IMAGE FUNCTIONS                                      */
703 /* ========================================================================= */
704
705 struct ImageInfo
706 {
707   char *source_filename;
708   int num_references;
709
710   Bitmap *bitmap;
711   boolean contains_small_images;
712 };
713 typedef struct ImageInfo ImageInfo;
714
715 static struct ArtworkListInfo *image_info = NULL;
716
717 static void *Load_PCX(char *filename)
718 {
719   ImageInfo *img_info;
720
721 #if 0
722   printf("loading PCX file '%s'\n", filename);
723 #endif
724
725   img_info = checked_calloc(sizeof(ImageInfo));
726
727   if ((img_info->bitmap = LoadImage(filename)) == NULL)
728   {
729     Error(ERR_WARN, "cannot read image file '%s': LoadImage() failed: %s",
730           filename, GetError());
731     free(img_info);
732     return NULL;
733   }
734
735   img_info->source_filename = getStringCopy(filename);
736
737   img_info->contains_small_images = FALSE;
738
739   return img_info;
740 }
741
742 static void FreeImage(void *ptr)
743 {
744   ImageInfo *image = (ImageInfo *)ptr;
745
746   if (image == NULL)
747     return;
748
749   if (image->bitmap)
750     FreeBitmap(image->bitmap);
751
752   if (image->source_filename)
753     free(image->source_filename);
754
755   free(image);
756 }
757
758 int getImageListSize()
759 {
760   return (image_info->num_file_list_entries +
761           image_info->num_dynamic_file_list_entries);
762 }
763
764 struct FileInfo *getImageListEntry(int pos)
765 {
766   int num_list_entries = image_info->num_file_list_entries;
767   int list_pos = (pos < num_list_entries ? pos : pos - num_list_entries);
768
769   return (pos < num_list_entries ? &image_info->file_list[list_pos] :
770           &image_info->dynamic_file_list[list_pos]);
771 }
772
773 static ImageInfo *getImageInfoEntryFromImageID(int pos)
774 {
775   int num_list_entries = image_info->num_file_list_entries;
776   int list_pos = (pos < num_list_entries ? pos : pos - num_list_entries);
777   ImageInfo **img_info =
778     (ImageInfo **)(pos < num_list_entries ? image_info->artwork_list :
779                    image_info->dynamic_artwork_list);
780
781   return img_info[list_pos];
782 }
783
784 Bitmap *getBitmapFromImageID(int pos)
785 {
786 #if 0
787   int num_list_entries = image_info->num_file_list_entries;
788   int list_pos = (pos < num_list_entries ? pos : pos - num_list_entries);
789   ImageInfo **img_info =
790     (ImageInfo **)(pos < num_list_entries ? image_info->artwork_list :
791                    image_info->dynamic_artwork_list);
792
793   return (img_info[list_pos] != NULL ? img_info[list_pos]->bitmap : NULL);
794 #else
795   ImageInfo *img_info = getImageInfoEntryFromImageID(pos);
796
797   return (img_info != NULL ? img_info->bitmap : NULL);
798 #endif
799 }
800
801 char *getTokenFromImageID(int graphic)
802 {
803   struct FileInfo *file_list = (struct FileInfo *)image_info->file_list;
804
805   return file_list[graphic].token;
806 }
807
808 char *getImageConfigFilename()
809 {
810   return getCustomArtworkConfigFilename(image_info->type);
811 }
812
813 int getImageListPropertyMappingSize()
814 {
815   return image_info->num_property_mapping_entries;
816 }
817
818 struct PropertyMapping *getImageListPropertyMapping()
819 {
820   return image_info->property_mapping;
821 }
822
823 void InitImageList(struct ConfigInfo *config_list, int num_file_list_entries,
824                    struct ConfigInfo *config_suffix_list,
825                    char **base_prefixes,
826                    char **ext1_suffixes,
827                    char **ext2_suffixes,
828                    char **ext3_suffixes)
829 {
830   int i;
831
832   image_info = checked_calloc(sizeof(struct ArtworkListInfo));
833   image_info->type = ARTWORK_TYPE_GRAPHICS;
834
835   /* ---------- initialize file list and suffix lists ---------- */
836
837   image_info->num_file_list_entries = num_file_list_entries;
838   image_info->num_dynamic_file_list_entries = 0;
839
840   image_info->file_list =
841     getFileListFromConfigList(config_list, config_suffix_list,
842                               num_file_list_entries);
843   image_info->dynamic_file_list = NULL;
844
845   image_info->num_suffix_list_entries = 0;
846   for (i=0; config_suffix_list[i].token != NULL; i++)
847     image_info->num_suffix_list_entries++;
848
849   image_info->suffix_list = config_suffix_list;
850
851   /* ---------- initialize base prefix and suffixes lists ---------- */
852
853   image_info->num_base_prefixes = 0;
854   for (i=0; base_prefixes[i] != NULL; i++)
855     image_info->num_base_prefixes++;
856
857   image_info->num_ext1_suffixes = 0;
858   for (i=0; ext1_suffixes[i] != NULL; i++)
859     image_info->num_ext1_suffixes++;
860
861   image_info->num_ext2_suffixes = 0;
862   for (i=0; ext2_suffixes[i] != NULL; i++)
863     image_info->num_ext2_suffixes++;
864
865   image_info->num_ext3_suffixes = 0;
866   for (i=0; ext3_suffixes[i] != NULL; i++)
867     image_info->num_ext3_suffixes++;
868
869   image_info->base_prefixes = base_prefixes;
870   image_info->ext1_suffixes = ext1_suffixes;
871   image_info->ext2_suffixes = ext2_suffixes;
872   image_info->ext3_suffixes = ext3_suffixes;
873
874   image_info->num_property_mapping_entries = 0;
875
876   image_info->property_mapping = NULL;
877
878   /* ---------- initialize artwork reference and content lists ---------- */
879
880   image_info->sizeof_artwork_list_entry = sizeof(ImageInfo *);
881
882   image_info->artwork_list =
883     checked_calloc(num_file_list_entries * sizeof(ImageInfo *));
884   image_info->dynamic_artwork_list = NULL;
885
886   image_info->content_list = NULL;
887
888   /* ---------- initialize artwork loading/freeing functions ---------- */
889
890   image_info->load_artwork = Load_PCX;
891   image_info->free_artwork = FreeImage;
892 }
893
894 void ReloadCustomImages()
895 {
896 #if 0
897   printf("DEBUG: reloading images '%s' ...\n", artwork.gfx_current_identifier);
898 #endif
899
900   LoadArtworkConfig(image_info);
901   ReloadCustomArtworkList(image_info);
902 }
903
904 void CreateImageWithSmallImages(int pos)
905 {
906   ImageInfo *img_info = getImageInfoEntryFromImageID(pos);
907
908   if (img_info == NULL || img_info->contains_small_images)
909     return;
910
911   CreateBitmapWithSmallBitmaps(img_info->bitmap);
912
913   img_info->contains_small_images = TRUE;
914 }
915
916 void FreeAllImages()
917 {
918   FreeCustomArtworkLists(image_info);
919 }