rnd-20040823-1-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   if ((mask_pixmap = XCreateBitmapFromData(display, window, (char *)mask_data,
140                                            image->width, image->height))
141       == None)
142     Error(ERR_EXIT, "Image_to_Mask(): XCreateBitmapFromData() failed");
143
144   free(mask_data);
145
146   return mask_pixmap;
147 }
148
149 Pixmap Pixmap_to_Mask(Pixmap src_pixmap, int src_width, int src_height)
150 {
151   XImage *src_ximage;
152   byte *src_ptr, *dst_ptr, *dst_ptr2;
153   int bits_per_pixel;
154   int bytes_per_pixel;
155   unsigned int bytes_per_row;
156   unsigned int x, y, i;
157   byte bitmask;
158   byte *mask_data;
159   Pixmap mask_pixmap;
160
161   /* copy source pixmap to temporary image */
162   if ((src_ximage = XGetImage(display, src_pixmap, 0, 0, src_width, src_height,
163                               AllPlanes, ZPixmap)) == NULL)
164     Error(ERR_EXIT, "Pixmap_to_Mask(): XGetImage() failed");
165
166   bits_per_pixel = src_ximage->bits_per_pixel;
167   bytes_per_pixel = (bits_per_pixel + 7) / 8;
168
169   bytes_per_row = (src_width + 7) / 8;
170   mask_data = checked_calloc(bytes_per_row * src_height);
171
172   src_ptr = (byte *)src_ximage->data;
173   dst_ptr = mask_data;
174
175   /* create bitmap data which can be used by 'XCreateBitmapFromData()'
176    * directly to create a pixmap of depth 1 for use as a clip mask for
177    * the corresponding image pixmap
178    */
179
180   for (y = 0; y < src_height; y++)
181   {
182     bitmask = 0x01;             /* start with leftmost bit in the byte     */
183     dst_ptr2 = dst_ptr;         /* start with leftmost byte in the row     */
184
185     for (x = 0; x < src_width; x++)
186     {
187       for (i = 0; i < bytes_per_pixel; i++)
188         if (*src_ptr++)         /* source pixel solid? (pixel index != 0)  */
189           *dst_ptr2 |= bitmask; /* then write a bit into the image mask    */
190
191       if ((bitmask <<= 1) == 0) /* bit at rightmost byte position reached? */
192       {
193         bitmask = 0x01;         /* start again with leftmost bit position  */
194         dst_ptr2++;             /* continue with next byte in image mask   */
195       }
196     }
197
198     dst_ptr += bytes_per_row;   /* continue with leftmost byte of next row */
199   }
200
201   if ((mask_pixmap = XCreateBitmapFromData(display, window->drawable,
202                                            (char *)mask_data,
203                                            src_width, src_height)) == None)
204     Error(ERR_EXIT, "Pixmap_to_Mask(): XCreateBitmapFromData() failed");
205
206   free(mask_data);
207
208   return mask_pixmap;
209 }
210
211 static int bitsPerPixelAtDepth(Display *display, int screen, int depth)
212 {
213   XPixmapFormatValues *pixmap_format;
214   int i, num_pixmap_formats, bits_per_pixel = -1;
215
216   /* get Pixmap formats supported by the X server */
217   pixmap_format = XListPixmapFormats(display, &num_pixmap_formats);
218
219   /* find format that matches the given depth */
220   for (i = 0; i < num_pixmap_formats; i++)
221     if (pixmap_format[i].depth == depth)
222       bits_per_pixel = pixmap_format[i].bits_per_pixel;
223
224   XFree(pixmap_format);
225
226   if (bits_per_pixel == -1)
227     Error(ERR_EXIT, "cannot find pixmap format for depth %d", depth);
228
229   return bits_per_pixel;
230 }
231
232 XImageInfo *Image_to_Pixmap(Display *display, int screen, Visual *visual,
233                             Window window, GC gc, int depth, Image *image)
234 {
235   static XColor xcolor_private[NOFLASH_COLORS];
236   static int colorcell_used[NOFLASH_COLORS];
237   static Colormap global_cmap = 0;
238   static Pixel *global_cmap_index;
239   static int num_cmap_entries, free_cmap_entries;
240   static boolean private_cmap = FALSE;
241   Pixel *redvalue, *greenvalue, *bluevalue;
242   unsigned int display_bytes_per_pixel, display_bits_per_pixel;
243   unsigned int a, c = 0, x, y;
244   XColor xcolor;
245   XImage *ximage;
246   XImageInfo *ximageinfo;
247   byte *src_ptr, *dst_ptr;
248   char *error = "Image_to_Pixmap(): %s";
249
250   if (image->type == IMAGETYPE_TRUECOLOR && depth == 8)
251   {
252     SetError(error, "cannot handle true-color images on 8-bit display");
253     return NULL;
254   }
255
256   if (!global_cmap)
257   {
258     if (visual == DefaultVisual(display, screen))
259       global_cmap = DefaultColormap(display, screen);
260     else
261     {
262       global_cmap = XCreateColormap(display, RootWindow(display, screen),
263                                     visual, AllocNone);
264       private_cmap = TRUE;
265     }
266   }
267
268   xcolor.flags = DoRed | DoGreen | DoBlue;
269   redvalue = greenvalue = bluevalue = NULL;
270   ximageinfo = checked_malloc(sizeof(XImageInfo));
271   ximageinfo->display = display;
272   ximageinfo->depth = depth;
273
274   switch (visual->class)
275   {
276     case TrueColor:
277     case DirectColor:
278     {
279       Pixel pixval;
280       unsigned int redcolors, greencolors, bluecolors;
281       unsigned int redstep, greenstep, bluestep;
282       unsigned int redbottom, greenbottom, bluebottom;
283       unsigned int redtop, greentop, bluetop;
284
285       redvalue = (Pixel *)checked_malloc(sizeof(Pixel) * 256);
286       greenvalue = (Pixel *)checked_malloc(sizeof(Pixel) * 256);
287       bluevalue = (Pixel *)checked_malloc(sizeof(Pixel) * 256);
288
289       ximageinfo->cmap = global_cmap;
290
291       retry_direct: /* tag we hit if a DirectColor allocation fails on
292                      * default colormap */
293
294       /* calculate number of distinct colors in each band */
295
296       redcolors = greencolors = bluecolors = 1;
297       for (pixval = 1; pixval; pixval <<= 1)
298       {
299         if (pixval & visual->red_mask)
300           redcolors <<= 1;
301         if (pixval & visual->green_mask)
302           greencolors <<= 1;
303         if (pixval & visual->blue_mask)
304           bluecolors <<= 1;
305       }
306       
307       /* consistency check */
308       if (redcolors > visual->map_entries ||
309           greencolors > visual->map_entries ||
310           bluecolors > visual->map_entries)
311         Error(ERR_WARN, "inconsistency in color information");
312
313       redstep = 256 / redcolors;
314       greenstep = 256 / greencolors;
315       bluestep = 256 / bluecolors;
316       redbottom = greenbottom = bluebottom = 0;
317       redtop = greentop = bluetop = 0;
318
319       for (a = 0; a < visual->map_entries; a++)
320       {
321         if (redbottom < 256)
322           redtop = redbottom + redstep;
323         if (greenbottom < 256)
324           greentop = greenbottom + greenstep;
325         if (bluebottom < 256)
326           bluetop = bluebottom + bluestep;
327
328         xcolor.red = (redtop - 1) << 8;
329         xcolor.green = (greentop - 1) << 8;
330         xcolor.blue = (bluetop - 1) << 8;
331         if (!XAllocColor(display, ximageinfo->cmap, &xcolor))
332         {
333           /* if an allocation fails for a DirectColor default visual then
334              we should create a private colormap and try again. */
335
336           if ((visual->class == DirectColor) &&
337               (visual == DefaultVisual(display, screen)))
338           {
339             global_cmap = XCopyColormapAndFree(display, global_cmap);
340             ximageinfo->cmap = global_cmap;
341             private_cmap = TRUE;
342
343             goto retry_direct;
344           }
345
346           /* something completely unexpected happened */
347
348           fprintf(stderr, "Image_to_Pixmap: XAllocColor failed on a TrueColor/Directcolor visual\n");
349
350           free(redvalue);
351           free(greenvalue);
352           free(bluevalue);
353           free(ximageinfo);
354
355           return NULL;
356         }
357
358         /* fill in pixel values for each band at this intensity */
359
360         while ((redbottom < 256) && (redbottom < redtop))
361           redvalue[redbottom++] = xcolor.pixel & visual->red_mask;
362         while ((greenbottom < 256) && (greenbottom < greentop))
363           greenvalue[greenbottom++] = xcolor.pixel & visual->green_mask;
364         while ((bluebottom < 256) && (bluebottom < bluetop))
365           bluevalue[bluebottom++] = xcolor.pixel & visual->blue_mask;
366       }
367
368       break;
369     }
370
371     case PseudoColor:
372
373       ximageinfo->cmap = global_cmap;
374
375       for (a = 0; a < MAX_COLORS; a++)
376       {
377         XColor xcolor2;
378         unsigned short mask;
379         int color_found;
380         int i;
381
382         if (!image->rgb.color_used[a])
383           continue;
384
385         xcolor.red = *(image->rgb.red + a);
386         xcolor.green = *(image->rgb.green + a);
387         xcolor.blue = *(image->rgb.blue + a);
388   
389         /* look if this color already exists in our colormap */
390         if (!XAllocColor(display, ximageinfo->cmap, &xcolor))
391         {
392           if (!private_cmap)
393           {
394             if (options.verbose)
395               Error(ERR_RETURN, "switching to private colormap");
396
397             /* we just filled up the default colormap -- get a private one
398                which contains all already allocated colors */
399
400             global_cmap = XCopyColormapAndFree(display, global_cmap);
401             ximageinfo->cmap = global_cmap;
402             private_cmap = TRUE;
403
404             /* allocate the rest of the color cells read/write */
405             global_cmap_index =
406               (Pixel *)checked_malloc(sizeof(Pixel) * NOFLASH_COLORS);
407             for (i = 0; i < NOFLASH_COLORS; i++)
408               if (!XAllocColorCells(display, global_cmap, FALSE, NULL, 0,
409                                     global_cmap_index + i, 1))
410                 break;
411             num_cmap_entries = free_cmap_entries = i;
412
413             /*
414             printf("We've got %d free colormap entries.\n", free_cmap_entries);
415             */
416
417             /* to minimize colormap flashing, copy default colors and try
418                to keep them as near as possible to the old values */
419
420             for (i = 0; i < num_cmap_entries; i++)
421             {
422               xcolor2.pixel = *(global_cmap_index + i);
423               XQueryColor(display, DefaultColormap(display, screen), &xcolor2);
424               XStoreColor(display, global_cmap, &xcolor2);
425               xcolor_private[xcolor2.pixel] = xcolor2;
426               colorcell_used[xcolor2.pixel] = FALSE;
427             }
428
429             /* now we have the default colormap private: all colors we
430                successfully allocated so far are read-only, which is okay,
431                because we don't want to change them anymore -- if we need
432                an existing color again, we get it by XAllocColor; all other
433                colors are read/write and we can set them by XStoreColor,
434                but we will try to overwrite those color cells with our new
435                color which are as close as possible to our new color */
436           }
437
438           /* look for an existing default color close the one we want */
439
440           mask = 0xf000;
441           color_found = FALSE;
442
443           while (!color_found)
444           {
445             for (i = num_cmap_entries - 1; i >= 0; i--)
446             {
447               xcolor2.pixel = *(global_cmap_index + i);
448               xcolor2 = xcolor_private[xcolor2.pixel];
449
450               if (colorcell_used[xcolor2.pixel])
451                 continue;
452
453               if ((xcolor.red & mask) == (xcolor2.red & mask) &&
454                   (xcolor.green & mask) == (xcolor2.green & mask) &&
455                   (xcolor.blue & mask) == (xcolor2.blue & mask))
456               {
457                 /*
458                 printf("replacing color cell %ld with a close color\n",
459                        xcolor2.pixel);
460                        */
461                 color_found = TRUE;
462                 break;
463               }
464             }
465
466             if (mask == 0x0000)
467               break;
468
469             mask = (mask << 1) & 0xffff;
470           }
471
472           if (!color_found)             /* no more free color cells */
473           {
474             SetError(error, "cannot allocate enough color cells");
475             return NULL;
476           }
477
478           xcolor.pixel = xcolor2.pixel;
479           xcolor_private[xcolor.pixel] = xcolor;
480           colorcell_used[xcolor.pixel] = TRUE;
481           XStoreColor(display, ximageinfo->cmap, &xcolor);
482           free_cmap_entries--;
483         }
484
485         *(ximageinfo->index + a) = xcolor.pixel;
486       }
487
488       /*
489       printf("still %d free colormap entries\n", free_cmap_entries);
490       */
491
492       ximageinfo->no = a;       /* number of pixels allocated for this image */
493       break;
494   
495     default:
496       Error(ERR_RETURN,"DirectColor, TrueColor or PseudoColor display needed");
497       SetError(error, "display class not supported");
498
499       return NULL;
500   }
501
502 #if DEBUG_TIMING
503   debug_print_timestamp(2, "   ALLOCATING IMAGE COLORS:   ");
504 #endif
505
506   /* create XImage from internal image structure and convert it to Pixmap */
507
508   display_bits_per_pixel = bitsPerPixelAtDepth(display, screen, depth);
509   display_bytes_per_pixel = (display_bits_per_pixel + 7) / 8;
510
511   ximage = XCreateImage(display, visual, depth, ZPixmap,
512                         0, NULL, image->width, image->height,
513                         8, image->width * display_bytes_per_pixel);
514   ximage->data =
515     checked_malloc(image->width * image->height * display_bytes_per_pixel);
516   ximage->byte_order = MSBFirst;
517
518   src_ptr = image->data;
519   dst_ptr = (byte *)ximage->data;
520
521   switch (visual->class)
522   {
523     case DirectColor:
524     case TrueColor:
525     {
526       Pixel pixval;
527
528       switch (image->type)
529       {
530         case IMAGETYPE_RGB:
531         {
532           for (y = 0; y < image->height; y++)           /* general case */
533           {
534             for (x = 0; x < image->width; x++)
535             {
536               pixval = *src_ptr++;
537               pixval =
538                 redvalue[image->rgb.red[pixval] >> 8] |
539                 greenvalue[image->rgb.green[pixval] >> 8] |
540                 bluevalue[image->rgb.blue[pixval] >> 8];
541               value_to_memory(pixval, dst_ptr, display_bytes_per_pixel);
542               dst_ptr += display_bytes_per_pixel;
543             }
544           }
545
546           break;
547         }
548
549         case IMAGETYPE_TRUECOLOR:
550         {
551           for (y = 0; y < image->height; y++)           /* general case */
552           {
553             for (x = 0; x < image->width; x++)
554             {
555               pixval = memory_to_value(src_ptr, image->bytes_per_pixel);
556               pixval =
557                 redvalue[TRUECOLOR_RED(pixval)] |
558                 greenvalue[TRUECOLOR_GREEN(pixval)] |
559                 bluevalue[TRUECOLOR_BLUE(pixval)];
560               value_to_memory(pixval, dst_ptr, display_bytes_per_pixel);
561               src_ptr += image->bytes_per_pixel;
562               dst_ptr += display_bytes_per_pixel;
563             }
564           }
565
566           break;
567         }
568
569         default:
570           Error(ERR_RETURN, "RGB or TrueColor image needed");
571           SetError(error, "image type not supported");
572
573           return NULL;
574       }
575
576       break;
577     }
578
579     case PseudoColor:
580     {
581       if (display_bytes_per_pixel == 1)         /* special case */
582       {
583         for (y = 0; y < image->height; y++)
584           for (x = 0; x < image->width; x++)
585             *dst_ptr++ = ximageinfo->index[c + *src_ptr++];
586       }
587       else                                      /* general case */
588       {
589         for (y = 0; y < image->height; y++)
590         {
591           for (x = 0; x < image->width; x++)
592           {
593             value_to_memory(ximageinfo->index[c + *src_ptr++],
594                             dst_ptr, display_bytes_per_pixel);
595             dst_ptr += display_bytes_per_pixel;
596           }
597         }
598       }
599
600       break;
601     }
602
603     default:
604       Error(ERR_RETURN,"DirectColor, TrueColor or PseudoColor display needed");
605       SetError(error, "display class not supported");
606
607       return NULL;
608   }
609
610   if (redvalue)
611   {
612     free((byte *)redvalue);
613     free((byte *)greenvalue);
614     free((byte *)bluevalue);
615   }
616
617 #if DEBUG_TIMING
618   debug_print_timestamp(2, "   CONVERTING IMAGE TO XIMAGE:");
619 #endif
620
621   ximageinfo->pixmap = XCreatePixmap(display, window,
622                                      ximage->width, ximage->height,
623                                      ximageinfo->depth);
624
625   XPutImage(ximageinfo->display, ximageinfo->pixmap, gc,
626             ximage, 0, 0, 0, 0, ximage->width, ximage->height);
627
628   X11DestroyImage(ximage);
629
630   return ximageinfo;
631 }
632
633 /*
634   -----------------------------------------------------------------------------
635   ZoomPixmap
636
637   Important note: The scaling code currently only supports scaling of the image
638   up or down by a power of 2 -- other scaling factors currently not supported!
639   Also not supported is scaling of pixmap masks (with depth 1); to scale them,
640   better use Pixmap_to_Mask() for now.
641   -----------------------------------------------------------------------------
642 */
643
644 void ZoomPixmap(Display *display, GC gc, Pixmap src_pixmap, Pixmap dst_pixmap,
645                 int src_width, int src_height,
646                 int dst_width, int dst_height)
647 {
648   XImage *src_ximage, *dst_ximage;
649   byte *src_ptr, *dst_ptr;
650   int bits_per_pixel;
651   int bytes_per_pixel;
652   int x, y, xx, yy, i;
653   int row_skip, col_skip;
654   int zoom_factor;
655   boolean scale_down = (src_width > dst_width);
656
657   if (scale_down)
658   {
659     zoom_factor = src_width / dst_width;
660
661     /* adjust source image size to integer multiple of destination size */
662     src_width  = dst_width  * zoom_factor;
663     src_height = dst_height * zoom_factor;
664   }
665   else
666   {
667     zoom_factor = dst_width / src_width;
668
669     /* no adjustment needed when scaling up (some pixels may be left blank) */
670   }
671
672   /* copy source pixmap to temporary image */
673   if ((src_ximage = XGetImage(display, src_pixmap, 0, 0, src_width, src_height,
674                               AllPlanes, ZPixmap)) == NULL)
675     Error(ERR_EXIT, "ZoomPixmap(): XGetImage() failed");
676
677   bits_per_pixel = src_ximage->bits_per_pixel;
678   bytes_per_pixel = (bits_per_pixel + 7) / 8;
679
680   if ((dst_ximage = XCreateImage(display, visual, src_ximage->depth, ZPixmap,
681                                  0, NULL, dst_width, dst_height,
682                                  8, dst_width * bytes_per_pixel)) == NULL)
683     Error(ERR_EXIT, "ZoomPixmap(): XCreateImage() failed");
684
685   dst_ximage->data =
686     checked_malloc(dst_width * dst_height * bytes_per_pixel);
687   dst_ximage->byte_order = src_ximage->byte_order;
688
689   src_ptr = (byte *)src_ximage->data;
690   dst_ptr = (byte *)dst_ximage->data;
691
692   if (scale_down)
693   {
694     col_skip = (zoom_factor - 1) * bytes_per_pixel;
695     row_skip = col_skip * src_width;
696
697     /* scale image down by scaling factor 'zoom_factor' */
698     for (y = 0; y < src_height; y += zoom_factor, src_ptr += row_skip)
699       for (x = 0; x < src_width; x += zoom_factor, src_ptr += col_skip)
700         for (i = 0; i < bytes_per_pixel; i++)
701           *dst_ptr++ = *src_ptr++;
702   }
703   else
704   {
705     row_skip = src_width * bytes_per_pixel;
706
707     /* scale image up by scaling factor 'zoom_factor' */
708     for (y = 0; y < src_height; y++)
709     {
710       for (yy = 0; yy < zoom_factor; yy++)
711       {
712         if (yy > 0)
713           src_ptr -= row_skip;
714
715         for (x = 0; x < src_width; x++)
716         {
717           for (xx = 0; xx < zoom_factor; xx++)
718             for (i = 0; i < bytes_per_pixel; i++)
719               *dst_ptr++ = *(src_ptr + i);
720
721           src_ptr += i;
722         }
723       }
724     }
725   }
726
727   /* copy scaled image to destination pixmap */
728   XPutImage(display, dst_pixmap, gc, dst_ximage, 0, 0, 0, 0,
729             dst_width, dst_height);
730
731   /* free temporary images */
732   X11DestroyImage(src_ximage);
733   X11DestroyImage(dst_ximage);
734 }
735
736 void freeXImage(Image *image, XImageInfo *ximageinfo)
737 {
738   if (ximageinfo->index != NULL && ximageinfo->no > 0)
739     XFreeColors(ximageinfo->display, ximageinfo->cmap, ximageinfo->index,
740                 ximageinfo->no, 0);
741   /* this       ^^^^^^^^^^^^^^ is wrong, because the used color cells
742    * are somewhere between 0 and MAX_COLORS; there are indeed 'ximageinfo->no'
743    * used color cells, but they are not at array position 0 - 'ximageinfo->no'
744    */
745
746   free(ximageinfo);
747 }
748
749 int Read_PCX_to_Pixmap(Display *display, Window window, GC gc, char *filename,
750                        Pixmap *pixmap, Pixmap *pixmap_mask)
751 {
752   Image *image;
753   XImageInfo *ximageinfo;
754   int screen;
755   Visual *visual;
756   int depth;
757
758 #if DEBUG_TIMING
759   debug_print_timestamp(2, NULL);       /* initialize timestamp function */
760 #endif
761
762   /* read the graphic file in PCX format to image structure */
763   if ((image = Read_PCX_to_Image(filename)) == NULL)
764     return errno_pcx;
765
766 #if DEBUG_TIMING
767   printf("%s:\n", filename);
768   debug_print_timestamp(2, "   READING PCX FILE TO IMAGE: ");
769 #endif
770
771   screen = DefaultScreen(display);
772   visual = DefaultVisual(display, screen);
773   depth = DefaultDepth(display, screen);
774
775   /* convert image structure to X11 Pixmap */
776   if (!(ximageinfo = Image_to_Pixmap(display, screen, visual,
777                                      window, gc, depth, image)))
778   {
779     freeImage(image);
780
781     return PCX_OtherError;
782   }
783
784   /* if a private colormap has been created, install it */
785   if (ximageinfo->cmap != DefaultColormap(display, screen))
786     XSetWindowColormap(display, window, ximageinfo->cmap);
787
788 #if DEBUG_TIMING
789   debug_print_timestamp(2, "   CONVERTING IMAGE TO PIXMAP:");
790 #endif
791
792   /* create clip mask for the image */
793   ximageinfo->pixmap_mask = Image_to_Mask(image, display, window);
794
795 #if DEBUG_TIMING
796   debug_print_timestamp(2, "   CONVERTING IMAGE TO MASK:  ");
797 #endif
798
799   *pixmap = ximageinfo->pixmap;
800   *pixmap_mask = ximageinfo->pixmap_mask;
801
802   /* free generic image and ximageinfo after native Pixmap has been created */
803   free(ximageinfo);
804   freeImage(image);
805
806   return PCX_Success;
807 }
808
809 #endif  /* PLATFORM_UNIX */
810 #endif  /* TARGET_X11 */
811
812
813 /* ========================================================================= */
814 /* PLATFORM INDEPENDENT IMAGE FUNCTIONS                                      */
815 /* ========================================================================= */
816
817 struct ImageInfo
818 {
819   char *source_filename;
820   int num_references;
821
822   Bitmap *bitmap;
823   boolean contains_small_images;
824 };
825 typedef struct ImageInfo ImageInfo;
826
827 static struct ArtworkListInfo *image_info = NULL;
828
829 static void *Load_PCX(char *filename)
830 {
831   ImageInfo *img_info;
832
833 #if 0
834   printf("loading PCX file '%s'\n", filename);
835 #endif
836
837   img_info = checked_calloc(sizeof(ImageInfo));
838
839   if ((img_info->bitmap = LoadImage(filename)) == NULL)
840   {
841     Error(ERR_WARN, "cannot load image file '%s': LoadImage() failed: %s",
842           filename, GetError());
843     free(img_info);
844     return NULL;
845   }
846
847   img_info->source_filename = getStringCopy(filename);
848
849   img_info->contains_small_images = FALSE;
850
851   return img_info;
852 }
853
854 static void FreeImage(void *ptr)
855 {
856   ImageInfo *image = (ImageInfo *)ptr;
857
858   if (image == NULL)
859     return;
860
861   if (image->bitmap)
862     FreeBitmap(image->bitmap);
863
864   if (image->source_filename)
865     free(image->source_filename);
866
867   free(image);
868 }
869
870 int getImageListSize()
871 {
872   return (image_info->num_file_list_entries +
873           image_info->num_dynamic_file_list_entries);
874 }
875
876 struct FileInfo *getImageListEntry(int pos)
877 {
878   int num_list_entries = image_info->num_file_list_entries;
879   int list_pos = (pos < num_list_entries ? pos : pos - num_list_entries);
880
881   return (pos < num_list_entries ? &image_info->file_list[list_pos] :
882           &image_info->dynamic_file_list[list_pos]);
883 }
884
885 static ImageInfo *getImageInfoEntryFromImageID(int pos)
886 {
887   int num_list_entries = image_info->num_file_list_entries;
888   int list_pos = (pos < num_list_entries ? pos : pos - num_list_entries);
889   ImageInfo **img_info =
890     (ImageInfo **)(pos < num_list_entries ? image_info->artwork_list :
891                    image_info->dynamic_artwork_list);
892
893   return img_info[list_pos];
894 }
895
896 Bitmap *getBitmapFromImageID(int pos)
897 {
898 #if 0
899   int num_list_entries = image_info->num_file_list_entries;
900   int list_pos = (pos < num_list_entries ? pos : pos - num_list_entries);
901   ImageInfo **img_info =
902     (ImageInfo **)(pos < num_list_entries ? image_info->artwork_list :
903                    image_info->dynamic_artwork_list);
904
905   return (img_info[list_pos] != NULL ? img_info[list_pos]->bitmap : NULL);
906 #else
907   ImageInfo *img_info = getImageInfoEntryFromImageID(pos);
908
909   return (img_info != NULL ? img_info->bitmap : NULL);
910 #endif
911 }
912
913 char *getTokenFromImageID(int graphic)
914 {
915 #if 0
916   /* !!! this does not work for dynamic artwork (crash!) !!! */
917   struct FileInfo *file_list = (struct FileInfo *)image_info->file_list;
918
919   return file_list[graphic].token;
920 #else
921   struct FileInfo *file_list = getImageListEntry(graphic);
922
923   return (file_list != NULL ? file_list->token : NULL);
924 #endif
925 }
926
927 int getImageIDFromToken(char *token)
928 {
929   struct FileInfo *file_list = image_info->file_list;
930   int num_list_entries = image_info->num_file_list_entries;
931   int i;
932
933   for (i = 0; i < num_list_entries; i++)
934     if (strcmp(file_list[i].token, token) == 0)
935       return i;
936
937   return -1;
938 }
939
940 char *getImageConfigFilename()
941 {
942   return getCustomArtworkConfigFilename(image_info->type);
943 }
944
945 int getImageListPropertyMappingSize()
946 {
947   return image_info->num_property_mapping_entries;
948 }
949
950 struct PropertyMapping *getImageListPropertyMapping()
951 {
952   return image_info->property_mapping;
953 }
954
955 void InitImageList(struct ConfigInfo *config_list, int num_file_list_entries,
956                    struct ConfigTypeInfo *config_suffix_list,
957                    char **base_prefixes, char **ext1_suffixes,
958                    char **ext2_suffixes, char **ext3_suffixes,
959                    char **ignore_tokens)
960 {
961   int i;
962
963   image_info = checked_calloc(sizeof(struct ArtworkListInfo));
964   image_info->type = ARTWORK_TYPE_GRAPHICS;
965
966   /* ---------- initialize file list and suffix lists ---------- */
967
968   image_info->num_file_list_entries = num_file_list_entries;
969   image_info->num_dynamic_file_list_entries = 0;
970
971   image_info->file_list =
972     getFileListFromConfigList(config_list, config_suffix_list, ignore_tokens,
973                               num_file_list_entries);
974   image_info->dynamic_file_list = NULL;
975
976   image_info->num_suffix_list_entries = 0;
977   for (i = 0; config_suffix_list[i].token != NULL; i++)
978     image_info->num_suffix_list_entries++;
979
980   image_info->suffix_list = config_suffix_list;
981
982   /* ---------- initialize base prefix and suffixes lists ---------- */
983
984   image_info->num_base_prefixes = 0;
985   for (i = 0; base_prefixes[i] != NULL; i++)
986     image_info->num_base_prefixes++;
987
988   image_info->num_ext1_suffixes = 0;
989   for (i = 0; ext1_suffixes[i] != NULL; i++)
990     image_info->num_ext1_suffixes++;
991
992   image_info->num_ext2_suffixes = 0;
993   for (i = 0; ext2_suffixes[i] != NULL; i++)
994     image_info->num_ext2_suffixes++;
995
996   image_info->num_ext3_suffixes = 0;
997   for (i = 0; ext3_suffixes[i] != NULL; i++)
998     image_info->num_ext3_suffixes++;
999
1000   image_info->num_ignore_tokens = 0;
1001   for (i = 0; ignore_tokens[i] != NULL; i++)
1002     image_info->num_ignore_tokens++;
1003
1004   image_info->base_prefixes = base_prefixes;
1005   image_info->ext1_suffixes = ext1_suffixes;
1006   image_info->ext2_suffixes = ext2_suffixes;
1007   image_info->ext3_suffixes = ext3_suffixes;
1008   image_info->ignore_tokens = ignore_tokens;
1009
1010   image_info->num_property_mapping_entries = 0;
1011
1012   image_info->property_mapping = NULL;
1013
1014   /* ---------- initialize artwork reference and content lists ---------- */
1015
1016   image_info->sizeof_artwork_list_entry = sizeof(ImageInfo *);
1017
1018   image_info->artwork_list =
1019     checked_calloc(num_file_list_entries * sizeof(ImageInfo *));
1020   image_info->dynamic_artwork_list = NULL;
1021
1022   image_info->content_list = NULL;
1023
1024   /* ---------- initialize artwork loading/freeing functions ---------- */
1025
1026   image_info->load_artwork = Load_PCX;
1027   image_info->free_artwork = FreeImage;
1028 }
1029
1030 void ReloadCustomImages()
1031 {
1032 #if 0
1033   printf("DEBUG: reloading images '%s' ...\n", artwork.gfx_current_identifier);
1034 #endif
1035
1036   LoadArtworkConfig(image_info);
1037   ReloadCustomArtworkList(image_info);
1038 }
1039
1040 void CreateImageWithSmallImages(int pos, int zoom_factor)
1041 {
1042   ImageInfo *img_info = getImageInfoEntryFromImageID(pos);
1043
1044   if (img_info == NULL || img_info->contains_small_images)
1045     return;
1046
1047   CreateBitmapWithSmallBitmaps(img_info->bitmap, zoom_factor);
1048
1049   img_info->contains_small_images = TRUE;
1050
1051 #if 0
1052   if (zoom_factor)
1053     printf("CreateImageWithSmallImages: '%s' zoomed by factor %d\n",
1054            img_info->source_filename, zoom_factor);
1055 #endif
1056
1057 #if 0
1058   printf("CreateImageWithSmallImages: '%s' done\n", img_info->source_filename);
1059 #endif
1060 }
1061
1062 void FreeAllImages()
1063 {
1064   FreeCustomArtworkLists(image_info);
1065 }