rnd-20030127-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 };
712 typedef struct ImageInfo ImageInfo;
713
714 static struct ArtworkListInfo *image_info = NULL;
715
716 static void *Load_PCX(char *filename)
717 {
718   ImageInfo *img_info;
719
720 #if 0
721   printf("loading PCX file '%s'\n", filename);
722 #endif
723
724   img_info = checked_calloc(sizeof(ImageInfo));
725
726   if ((img_info->bitmap = LoadImage(filename)) == NULL)
727   {
728     Error(ERR_WARN, "cannot read image file '%s': LoadImage() failed: %s",
729           filename, GetError());
730     free(img_info);
731     return NULL;
732   }
733
734   img_info->source_filename = getStringCopy(filename);
735
736   return img_info;
737 }
738
739 static void FreeImage(void *ptr)
740 {
741   ImageInfo *image = (ImageInfo *)ptr;
742
743   if (image == NULL)
744     return;
745
746   if (image->bitmap)
747     FreeBitmap(image->bitmap);
748
749   if (image->source_filename)
750     free(image->source_filename);
751
752   free(image);
753 }
754
755 int getImageListSize()
756 {
757   return (image_info->num_file_list_entries +
758           image_info->num_dynamic_file_list_entries);
759 }
760
761 struct FileInfo *getImageListEntry(int pos)
762 {
763   int num_list_entries = image_info->num_file_list_entries;
764   int list_pos = (pos < num_list_entries ? pos : pos - num_list_entries);
765
766   return (pos < num_list_entries ? &image_info->file_list[list_pos] :
767           &image_info->dynamic_file_list[list_pos]);
768 }
769
770 Bitmap *getBitmapFromImageID(int pos)
771 {
772   int num_list_entries = image_info->num_file_list_entries;
773   int list_pos = (pos < num_list_entries ? pos : pos - num_list_entries);
774   ImageInfo **img_info =
775     (ImageInfo **)(pos < num_list_entries ? image_info->artwork_list :
776                    image_info->dynamic_artwork_list);
777
778   return (img_info[list_pos] != NULL ? img_info[list_pos]->bitmap : NULL);
779 }
780
781 char *getTokenFromImageID(int graphic)
782 {
783   struct FileInfo *file_list = (struct FileInfo *)image_info->file_list;
784
785   return file_list[graphic].token;
786 }
787
788 char *getImageConfigFilename()
789 {
790   return getCustomArtworkConfigFilename(image_info->type);
791 }
792
793 int getImageListPropertyMappingSize()
794 {
795   return image_info->num_property_mapping_entries;
796 }
797
798 struct PropertyMapping *getImageListPropertyMapping()
799 {
800   return image_info->property_mapping;
801 }
802
803 void InitImageList(struct ConfigInfo *config_list, int num_file_list_entries,
804                    struct ConfigInfo *config_suffix_list,
805                    char **base_prefixes,
806                    char **ext1_suffixes,
807                    char **ext2_suffixes)
808 {
809   int i;
810
811   image_info = checked_calloc(sizeof(struct ArtworkListInfo));
812   image_info->type = ARTWORK_TYPE_GRAPHICS;
813
814   /* ---------- initialize file list and suffix lists ---------- */
815
816   image_info->num_file_list_entries = num_file_list_entries;
817   image_info->num_dynamic_file_list_entries = 0;
818
819   image_info->file_list =
820     getFileListFromConfigList(config_list, config_suffix_list,
821                               num_file_list_entries);
822   image_info->dynamic_file_list = NULL;
823
824   image_info->num_suffix_list_entries = 0;
825   for (i=0; config_suffix_list[i].token != NULL; i++)
826     image_info->num_suffix_list_entries++;
827
828   image_info->suffix_list = config_suffix_list;
829
830   /* ---------- initialize base prefix and suffixes lists ---------- */
831
832   image_info->num_base_prefixes = 0;
833   for (i=0; base_prefixes[i] != NULL; i++)
834     image_info->num_base_prefixes++;
835
836   image_info->num_ext1_suffixes = 0;
837   for (i=0; ext1_suffixes[i] != NULL; i++)
838     image_info->num_ext1_suffixes++;
839
840   image_info->num_ext2_suffixes = 0;
841   for (i=0; ext2_suffixes[i] != NULL; i++)
842     image_info->num_ext2_suffixes++;
843
844   image_info->base_prefixes = base_prefixes;
845   image_info->ext1_suffixes = ext1_suffixes;
846   image_info->ext2_suffixes = ext2_suffixes;
847
848   image_info->num_property_mapping_entries = 0;
849
850   image_info->property_mapping = NULL;
851
852   /* ---------- initialize artwork reference and content lists ---------- */
853
854   image_info->sizeof_artwork_list_entry = sizeof(ImageInfo *);
855
856   image_info->artwork_list =
857     checked_calloc(num_file_list_entries * sizeof(ImageInfo *));
858   image_info->dynamic_artwork_list = NULL;
859
860   image_info->content_list = NULL;
861
862   /* ---------- initialize artwork loading/freeing functions ---------- */
863
864   image_info->load_artwork = Load_PCX;
865   image_info->free_artwork = FreeImage;
866 }
867
868 void dumpImages()
869 {
870   struct ListNode *node;
871
872   if (image_info->content_list == NULL)
873     return;
874
875   for (node = image_info->content_list; node != NULL; node = node->next)
876   {
877     ImageInfo *img_info = (ImageInfo *)node->content;
878
879     printf("---> '%s' [%d]\n",
880            img_info->source_filename,
881            img_info->num_references);
882   }
883 }
884
885 void ReloadCustomImages()
886 {
887 #if 0
888   printf("DEBUG: reloading images '%s' ...\n", artwork.gfx_current_identifier);
889 #endif
890
891   LoadArtworkConfig(image_info);
892   ReloadCustomArtworkList(image_info);
893
894   dumpImages();
895 }
896
897 void FreeAllImages()
898 {
899   FreeCustomArtworkLists(image_info);
900 }