rnd-20040816-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   X11DestroyImage(ximage);
564
565   return ximageinfo;
566 }
567
568 /*
569   -----------------------------------------------------------------------------
570   ZoomPixmap
571
572   Important note: The scaling code currently only supports scaling of the image
573   up or down by a power of 2 -- other scaling factors currently not supported!
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, xx, yy, i;
586 #if 1
587   boolean scale_down = (src_width > dst_width);
588   int zoom_factor;
589 #else
590   int zoom_factor = src_width / dst_width;      /* currently very limited! */
591 #endif
592   int row_skip, col_skip;
593
594   if (scale_down)
595   {
596     zoom_factor = src_width / dst_width;
597
598     /* adjust source image size to integer multiple of destination size */
599     src_width  = dst_width  * zoom_factor;
600     src_height = dst_height * zoom_factor;
601   }
602   else
603   {
604     zoom_factor = dst_width / src_width;
605
606     /* no adjustment needed when scaling up (some pixels may be left blank) */
607   }
608
609   /* copy source pixmap to temporary image */
610   src_ximage = XGetImage(display, src_pixmap, 0, 0, src_width, src_height,
611                          AllPlanes, ZPixmap);
612
613   bits_per_pixel = src_ximage->bits_per_pixel;
614   bytes_per_pixel = (bits_per_pixel + 7) / 8;
615
616   dst_ximage = XCreateImage(display, visual, src_ximage->depth, ZPixmap,
617                             0, NULL, dst_width, dst_height,
618                             8, dst_width * bytes_per_pixel);
619   dst_ximage->data =
620     checked_malloc(dst_width * dst_height * bytes_per_pixel);
621   dst_ximage->byte_order = src_ximage->byte_order;
622
623   src_ptr = (byte *)src_ximage->data;
624   dst_ptr = (byte *)dst_ximage->data;
625
626   if (scale_down)
627   {
628     col_skip = (zoom_factor - 1) * bytes_per_pixel;
629     row_skip = col_skip * src_width;
630
631     /* scale image down by scaling factor 'zoom_factor' */
632     for (y = 0; y < src_height; y += zoom_factor, src_ptr += row_skip)
633       for (x = 0; x < src_width; x += zoom_factor, src_ptr += col_skip)
634         for (i = 0; i < bytes_per_pixel; i++)
635           *dst_ptr++ = *src_ptr++;
636   }
637   else
638   {
639     row_skip = src_width * bytes_per_pixel;
640
641     /* scale image up by scaling factor 'zoom_factor' */
642     for (y = 0; y < src_height; y++)
643     {
644       for (yy = 0; yy < zoom_factor; yy++)
645       {
646         if (yy > 0)
647           src_ptr -= row_skip;
648
649         for (x = 0; x < src_width; x++)
650         {
651           for (xx = 0; xx < zoom_factor; xx++)
652             for (i = 0; i < bytes_per_pixel; i++)
653               *dst_ptr++ = *(src_ptr + i);
654
655           src_ptr += i;
656         }
657       }
658     }
659   }
660
661   /* copy scaled image to destination pixmap */
662   XPutImage(display, dst_pixmap, gc, dst_ximage, 0, 0, 0, 0,
663             dst_width, dst_height);
664
665   /* free temporary images */
666   X11DestroyImage(src_ximage);
667   X11DestroyImage(dst_ximage);
668 }
669
670 void freeXImage(Image *image, XImageInfo *ximageinfo)
671 {
672   if (ximageinfo->index != NULL && ximageinfo->no > 0)
673     XFreeColors(ximageinfo->display, ximageinfo->cmap, ximageinfo->index,
674                 ximageinfo->no, 0);
675   /* this       ^^^^^^^^^^^^^^ is wrong, because the used color cells
676    * are somewhere between 0 and MAX_COLORS; there are indeed 'ximageinfo->no'
677    * used color cells, but they are not at array position 0 - 'ximageinfo->no'
678    */
679
680   free(ximageinfo);
681 }
682
683 int Read_PCX_to_Pixmap(Display *display, Window window, GC gc, char *filename,
684                        Pixmap *pixmap, Pixmap *pixmap_mask)
685 {
686   Image *image;
687   XImageInfo *ximageinfo;
688   int screen;
689   Visual *visual;
690   int depth;
691
692 #if DEBUG_TIMING
693   debug_print_timestamp(2, NULL);       /* initialize timestamp function */
694 #endif
695
696   /* read the graphic file in PCX format to image structure */
697   if ((image = Read_PCX_to_Image(filename)) == NULL)
698     return errno_pcx;
699
700 #if DEBUG_TIMING
701   printf("%s:\n", filename);
702   debug_print_timestamp(2, "   READING PCX FILE TO IMAGE: ");
703 #endif
704
705   screen = DefaultScreen(display);
706   visual = DefaultVisual(display, screen);
707   depth = DefaultDepth(display, screen);
708
709   /* convert image structure to X11 Pixmap */
710   if (!(ximageinfo = Image_to_Pixmap(display, screen, visual,
711                                      window, gc, depth, image)))
712   {
713     freeImage(image);
714
715     return PCX_OtherError;
716   }
717
718   /* if a private colormap has been created, install it */
719   if (ximageinfo->cmap != DefaultColormap(display, screen))
720     XSetWindowColormap(display, window, ximageinfo->cmap);
721
722 #if DEBUG_TIMING
723   debug_print_timestamp(2, "   CONVERTING IMAGE TO PIXMAP:");
724 #endif
725
726   /* create clip mask for the image */
727   ximageinfo->pixmap_mask = Image_to_Mask(image, display, window);
728
729 #if DEBUG_TIMING
730   debug_print_timestamp(2, "   CONVERTING IMAGE TO MASK:  ");
731 #endif
732
733   *pixmap = ximageinfo->pixmap;
734   *pixmap_mask = ximageinfo->pixmap_mask;
735
736   /* free generic image and ximageinfo after native Pixmap has been created */
737   free(ximageinfo);
738   freeImage(image);
739
740   return PCX_Success;
741 }
742
743 #endif  /* PLATFORM_UNIX */
744 #endif  /* TARGET_X11 */
745
746
747 /* ========================================================================= */
748 /* PLATFORM INDEPENDENT IMAGE FUNCTIONS                                      */
749 /* ========================================================================= */
750
751 struct ImageInfo
752 {
753   char *source_filename;
754   int num_references;
755
756   Bitmap *bitmap;
757   boolean contains_small_images;
758 };
759 typedef struct ImageInfo ImageInfo;
760
761 static struct ArtworkListInfo *image_info = NULL;
762
763 static void *Load_PCX(char *filename)
764 {
765   ImageInfo *img_info;
766
767 #if 0
768   printf("loading PCX file '%s'\n", filename);
769 #endif
770
771   img_info = checked_calloc(sizeof(ImageInfo));
772
773   if ((img_info->bitmap = LoadImage(filename)) == NULL)
774   {
775     Error(ERR_WARN, "cannot load image file '%s': LoadImage() failed: %s",
776           filename, GetError());
777     free(img_info);
778     return NULL;
779   }
780
781   img_info->source_filename = getStringCopy(filename);
782
783   img_info->contains_small_images = FALSE;
784
785   return img_info;
786 }
787
788 static void FreeImage(void *ptr)
789 {
790   ImageInfo *image = (ImageInfo *)ptr;
791
792   if (image == NULL)
793     return;
794
795   if (image->bitmap)
796     FreeBitmap(image->bitmap);
797
798   if (image->source_filename)
799     free(image->source_filename);
800
801   free(image);
802 }
803
804 int getImageListSize()
805 {
806   return (image_info->num_file_list_entries +
807           image_info->num_dynamic_file_list_entries);
808 }
809
810 struct FileInfo *getImageListEntry(int pos)
811 {
812   int num_list_entries = image_info->num_file_list_entries;
813   int list_pos = (pos < num_list_entries ? pos : pos - num_list_entries);
814
815   return (pos < num_list_entries ? &image_info->file_list[list_pos] :
816           &image_info->dynamic_file_list[list_pos]);
817 }
818
819 static ImageInfo *getImageInfoEntryFromImageID(int pos)
820 {
821   int num_list_entries = image_info->num_file_list_entries;
822   int list_pos = (pos < num_list_entries ? pos : pos - num_list_entries);
823   ImageInfo **img_info =
824     (ImageInfo **)(pos < num_list_entries ? image_info->artwork_list :
825                    image_info->dynamic_artwork_list);
826
827   return img_info[list_pos];
828 }
829
830 Bitmap *getBitmapFromImageID(int pos)
831 {
832 #if 0
833   int num_list_entries = image_info->num_file_list_entries;
834   int list_pos = (pos < num_list_entries ? pos : pos - num_list_entries);
835   ImageInfo **img_info =
836     (ImageInfo **)(pos < num_list_entries ? image_info->artwork_list :
837                    image_info->dynamic_artwork_list);
838
839   return (img_info[list_pos] != NULL ? img_info[list_pos]->bitmap : NULL);
840 #else
841   ImageInfo *img_info = getImageInfoEntryFromImageID(pos);
842
843   return (img_info != NULL ? img_info->bitmap : NULL);
844 #endif
845 }
846
847 char *getTokenFromImageID(int graphic)
848 {
849 #if 0
850   /* !!! this does not work for dynamic artwork (crash!) !!! */
851   struct FileInfo *file_list = (struct FileInfo *)image_info->file_list;
852
853   return file_list[graphic].token;
854 #else
855   struct FileInfo *file_list = getImageListEntry(graphic);
856
857   return (file_list != NULL ? file_list->token : NULL);
858 #endif
859 }
860
861 int getImageIDFromToken(char *token)
862 {
863   struct FileInfo *file_list = image_info->file_list;
864   int num_list_entries = image_info->num_file_list_entries;
865   int i;
866
867   for (i = 0; i < num_list_entries; i++)
868     if (strcmp(file_list[i].token, token) == 0)
869       return i;
870
871   return -1;
872 }
873
874 char *getImageConfigFilename()
875 {
876   return getCustomArtworkConfigFilename(image_info->type);
877 }
878
879 int getImageListPropertyMappingSize()
880 {
881   return image_info->num_property_mapping_entries;
882 }
883
884 struct PropertyMapping *getImageListPropertyMapping()
885 {
886   return image_info->property_mapping;
887 }
888
889 void InitImageList(struct ConfigInfo *config_list, int num_file_list_entries,
890                    struct ConfigTypeInfo *config_suffix_list,
891                    char **base_prefixes, char **ext1_suffixes,
892                    char **ext2_suffixes, char **ext3_suffixes,
893                    char **ignore_tokens)
894 {
895   int i;
896
897   image_info = checked_calloc(sizeof(struct ArtworkListInfo));
898   image_info->type = ARTWORK_TYPE_GRAPHICS;
899
900   /* ---------- initialize file list and suffix lists ---------- */
901
902   image_info->num_file_list_entries = num_file_list_entries;
903   image_info->num_dynamic_file_list_entries = 0;
904
905   image_info->file_list =
906     getFileListFromConfigList(config_list, config_suffix_list, ignore_tokens,
907                               num_file_list_entries);
908   image_info->dynamic_file_list = NULL;
909
910   image_info->num_suffix_list_entries = 0;
911   for (i = 0; config_suffix_list[i].token != NULL; i++)
912     image_info->num_suffix_list_entries++;
913
914   image_info->suffix_list = config_suffix_list;
915
916   /* ---------- initialize base prefix and suffixes lists ---------- */
917
918   image_info->num_base_prefixes = 0;
919   for (i = 0; base_prefixes[i] != NULL; i++)
920     image_info->num_base_prefixes++;
921
922   image_info->num_ext1_suffixes = 0;
923   for (i = 0; ext1_suffixes[i] != NULL; i++)
924     image_info->num_ext1_suffixes++;
925
926   image_info->num_ext2_suffixes = 0;
927   for (i = 0; ext2_suffixes[i] != NULL; i++)
928     image_info->num_ext2_suffixes++;
929
930   image_info->num_ext3_suffixes = 0;
931   for (i = 0; ext3_suffixes[i] != NULL; i++)
932     image_info->num_ext3_suffixes++;
933
934   image_info->num_ignore_tokens = 0;
935   for (i = 0; ignore_tokens[i] != NULL; i++)
936     image_info->num_ignore_tokens++;
937
938   image_info->base_prefixes = base_prefixes;
939   image_info->ext1_suffixes = ext1_suffixes;
940   image_info->ext2_suffixes = ext2_suffixes;
941   image_info->ext3_suffixes = ext3_suffixes;
942   image_info->ignore_tokens = ignore_tokens;
943
944   image_info->num_property_mapping_entries = 0;
945
946   image_info->property_mapping = NULL;
947
948   /* ---------- initialize artwork reference and content lists ---------- */
949
950   image_info->sizeof_artwork_list_entry = sizeof(ImageInfo *);
951
952   image_info->artwork_list =
953     checked_calloc(num_file_list_entries * sizeof(ImageInfo *));
954   image_info->dynamic_artwork_list = NULL;
955
956   image_info->content_list = NULL;
957
958   /* ---------- initialize artwork loading/freeing functions ---------- */
959
960   image_info->load_artwork = Load_PCX;
961   image_info->free_artwork = FreeImage;
962 }
963
964 void ReloadCustomImages()
965 {
966 #if 0
967   printf("DEBUG: reloading images '%s' ...\n", artwork.gfx_current_identifier);
968 #endif
969
970   LoadArtworkConfig(image_info);
971   ReloadCustomArtworkList(image_info);
972 }
973
974 void CreateImageWithSmallImages(int pos)
975 {
976   ImageInfo *img_info = getImageInfoEntryFromImageID(pos);
977
978   if (img_info == NULL || img_info->contains_small_images)
979     return;
980
981   CreateBitmapWithSmallBitmaps(img_info->bitmap);
982
983   img_info->contains_small_images = TRUE;
984
985 #if 0
986   printf("CreateImageWithSmallImages: '%s' done\n", img_info->source_filename);
987 #endif
988 }
989
990 void FreeAllImages()
991 {
992   FreeCustomArtworkLists(image_info);
993 }