rnd-20040819-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   -----------------------------------------------------------------------------
640 */
641
642 void ZoomPixmap(Display *display, GC gc, Pixmap src_pixmap, Pixmap dst_pixmap,
643                 int src_width, int src_height,
644                 int dst_width, int dst_height)
645 {
646   XImage *src_ximage, *dst_ximage;
647   byte *src_ptr, *dst_ptr;
648   int bits_per_pixel;
649   int bytes_per_pixel;
650   int x, y, xx, yy, i;
651 #if 1
652   boolean scale_down = (src_width > dst_width);
653   int zoom_factor;
654 #else
655   int zoom_factor = src_width / dst_width;      /* currently very limited! */
656 #endif
657   int row_skip, col_skip;
658
659   if (scale_down)
660   {
661     zoom_factor = src_width / dst_width;
662
663     /* adjust source image size to integer multiple of destination size */
664     src_width  = dst_width  * zoom_factor;
665     src_height = dst_height * zoom_factor;
666   }
667   else
668   {
669     zoom_factor = dst_width / src_width;
670
671     /* no adjustment needed when scaling up (some pixels may be left blank) */
672   }
673
674   /* copy source pixmap to temporary image */
675   if ((src_ximage = XGetImage(display, src_pixmap, 0, 0, src_width, src_height,
676                               AllPlanes, ZPixmap)) == NULL)
677     Error(ERR_EXIT, "ZoomPixmap(): XGetImage() failed");
678
679   bits_per_pixel = src_ximage->bits_per_pixel;
680   bytes_per_pixel = (bits_per_pixel + 7) / 8;
681
682   if ((dst_ximage = XCreateImage(display, visual, src_ximage->depth, ZPixmap,
683                                  0, NULL, dst_width, dst_height,
684                                  8, dst_width * bytes_per_pixel)) == NULL)
685     Error(ERR_EXIT, "ZoomPixmap(): XCreateImage() failed");
686
687   dst_ximage->data =
688     checked_malloc(dst_width * dst_height * bytes_per_pixel);
689   dst_ximage->byte_order = src_ximage->byte_order;
690
691   src_ptr = (byte *)src_ximage->data;
692   dst_ptr = (byte *)dst_ximage->data;
693
694   if (scale_down)
695   {
696     col_skip = (zoom_factor - 1) * bytes_per_pixel;
697     row_skip = col_skip * src_width;
698
699     /* scale image down by scaling factor 'zoom_factor' */
700     for (y = 0; y < src_height; y += zoom_factor, src_ptr += row_skip)
701       for (x = 0; x < src_width; x += zoom_factor, src_ptr += col_skip)
702         for (i = 0; i < bytes_per_pixel; i++)
703           *dst_ptr++ = *src_ptr++;
704   }
705   else
706   {
707     row_skip = src_width * bytes_per_pixel;
708
709 #if 0
710     printf("::: %d, %d -> %d, %d [%d / %d]\n[%ld -> %ld (%ld)] [%ld -> %ld (%ld)]\n",
711            src_width, src_height,
712            dst_width, dst_height,
713            zoom_factor, bytes_per_pixel,
714            src_ptr,
715            src_ptr + src_width * src_height * bytes_per_pixel,
716            src_width * src_height * bytes_per_pixel,
717            dst_ptr,
718            dst_ptr + dst_width * dst_height * bytes_per_pixel,
719            dst_width * dst_height * bytes_per_pixel);
720
721     printf("A\n");
722
723     for (i = 0; i < src_width * src_height * bytes_per_pixel;
724          i++)
725     {
726       byte x = *(byte *)(src_ptr + i);
727
728       printf("::: %d ...\n", i);
729
730       x = x * 1;
731     }
732
733     printf("B\n");
734 #endif
735
736     /* scale image up by scaling factor 'zoom_factor' */
737     for (y = 0; y < src_height; y++)
738     {
739       for (yy = 0; yy < zoom_factor; yy++)
740       {
741         if (yy > 0)
742           src_ptr -= row_skip;
743
744 #if 0
745         printf("::: [%d -> %ld / %ld]\n", y, src_ptr, dst_ptr);
746 #endif
747
748         for (x = 0; x < src_width; x++)
749         {
750           for (xx = 0; xx < zoom_factor; xx++)
751             for (i = 0; i < bytes_per_pixel; i++)
752 #if 1
753               *dst_ptr++ = *(src_ptr + i);
754 #else
755               *dst_ptr++ = 0;
756 #endif
757
758           src_ptr += i;
759         }
760       }
761     }
762   }
763
764   /* copy scaled image to destination pixmap */
765   XPutImage(display, dst_pixmap, gc, dst_ximage, 0, 0, 0, 0,
766             dst_width, dst_height);
767
768   /* free temporary images */
769   X11DestroyImage(src_ximage);
770   X11DestroyImage(dst_ximage);
771 }
772
773 void freeXImage(Image *image, XImageInfo *ximageinfo)
774 {
775   if (ximageinfo->index != NULL && ximageinfo->no > 0)
776     XFreeColors(ximageinfo->display, ximageinfo->cmap, ximageinfo->index,
777                 ximageinfo->no, 0);
778   /* this       ^^^^^^^^^^^^^^ is wrong, because the used color cells
779    * are somewhere between 0 and MAX_COLORS; there are indeed 'ximageinfo->no'
780    * used color cells, but they are not at array position 0 - 'ximageinfo->no'
781    */
782
783   free(ximageinfo);
784 }
785
786 int Read_PCX_to_Pixmap(Display *display, Window window, GC gc, char *filename,
787                        Pixmap *pixmap, Pixmap *pixmap_mask)
788 {
789   Image *image;
790   XImageInfo *ximageinfo;
791   int screen;
792   Visual *visual;
793   int depth;
794
795 #if DEBUG_TIMING
796   debug_print_timestamp(2, NULL);       /* initialize timestamp function */
797 #endif
798
799   /* read the graphic file in PCX format to image structure */
800   if ((image = Read_PCX_to_Image(filename)) == NULL)
801     return errno_pcx;
802
803 #if DEBUG_TIMING
804   printf("%s:\n", filename);
805   debug_print_timestamp(2, "   READING PCX FILE TO IMAGE: ");
806 #endif
807
808   screen = DefaultScreen(display);
809   visual = DefaultVisual(display, screen);
810   depth = DefaultDepth(display, screen);
811
812   /* convert image structure to X11 Pixmap */
813   if (!(ximageinfo = Image_to_Pixmap(display, screen, visual,
814                                      window, gc, depth, image)))
815   {
816     freeImage(image);
817
818     return PCX_OtherError;
819   }
820
821   /* if a private colormap has been created, install it */
822   if (ximageinfo->cmap != DefaultColormap(display, screen))
823     XSetWindowColormap(display, window, ximageinfo->cmap);
824
825 #if DEBUG_TIMING
826   debug_print_timestamp(2, "   CONVERTING IMAGE TO PIXMAP:");
827 #endif
828
829   /* create clip mask for the image */
830   ximageinfo->pixmap_mask = Image_to_Mask(image, display, window);
831
832 #if DEBUG_TIMING
833   debug_print_timestamp(2, "   CONVERTING IMAGE TO MASK:  ");
834 #endif
835
836   *pixmap = ximageinfo->pixmap;
837   *pixmap_mask = ximageinfo->pixmap_mask;
838
839   /* free generic image and ximageinfo after native Pixmap has been created */
840   free(ximageinfo);
841   freeImage(image);
842
843   return PCX_Success;
844 }
845
846 #endif  /* PLATFORM_UNIX */
847 #endif  /* TARGET_X11 */
848
849
850 /* ========================================================================= */
851 /* PLATFORM INDEPENDENT IMAGE FUNCTIONS                                      */
852 /* ========================================================================= */
853
854 struct ImageInfo
855 {
856   char *source_filename;
857   int num_references;
858
859   Bitmap *bitmap;
860   boolean contains_small_images;
861 };
862 typedef struct ImageInfo ImageInfo;
863
864 static struct ArtworkListInfo *image_info = NULL;
865
866 static void *Load_PCX(char *filename)
867 {
868   ImageInfo *img_info;
869
870 #if 0
871   printf("loading PCX file '%s'\n", filename);
872 #endif
873
874   img_info = checked_calloc(sizeof(ImageInfo));
875
876   if ((img_info->bitmap = LoadImage(filename)) == NULL)
877   {
878     Error(ERR_WARN, "cannot load image file '%s': LoadImage() failed: %s",
879           filename, GetError());
880     free(img_info);
881     return NULL;
882   }
883
884   img_info->source_filename = getStringCopy(filename);
885
886   img_info->contains_small_images = FALSE;
887
888   return img_info;
889 }
890
891 static void FreeImage(void *ptr)
892 {
893   ImageInfo *image = (ImageInfo *)ptr;
894
895   if (image == NULL)
896     return;
897
898   if (image->bitmap)
899     FreeBitmap(image->bitmap);
900
901   if (image->source_filename)
902     free(image->source_filename);
903
904   free(image);
905 }
906
907 int getImageListSize()
908 {
909   return (image_info->num_file_list_entries +
910           image_info->num_dynamic_file_list_entries);
911 }
912
913 struct FileInfo *getImageListEntry(int pos)
914 {
915   int num_list_entries = image_info->num_file_list_entries;
916   int list_pos = (pos < num_list_entries ? pos : pos - num_list_entries);
917
918   return (pos < num_list_entries ? &image_info->file_list[list_pos] :
919           &image_info->dynamic_file_list[list_pos]);
920 }
921
922 static ImageInfo *getImageInfoEntryFromImageID(int pos)
923 {
924   int num_list_entries = image_info->num_file_list_entries;
925   int list_pos = (pos < num_list_entries ? pos : pos - num_list_entries);
926   ImageInfo **img_info =
927     (ImageInfo **)(pos < num_list_entries ? image_info->artwork_list :
928                    image_info->dynamic_artwork_list);
929
930   return img_info[list_pos];
931 }
932
933 Bitmap *getBitmapFromImageID(int pos)
934 {
935 #if 0
936   int num_list_entries = image_info->num_file_list_entries;
937   int list_pos = (pos < num_list_entries ? pos : pos - num_list_entries);
938   ImageInfo **img_info =
939     (ImageInfo **)(pos < num_list_entries ? image_info->artwork_list :
940                    image_info->dynamic_artwork_list);
941
942   return (img_info[list_pos] != NULL ? img_info[list_pos]->bitmap : NULL);
943 #else
944   ImageInfo *img_info = getImageInfoEntryFromImageID(pos);
945
946   return (img_info != NULL ? img_info->bitmap : NULL);
947 #endif
948 }
949
950 char *getTokenFromImageID(int graphic)
951 {
952 #if 0
953   /* !!! this does not work for dynamic artwork (crash!) !!! */
954   struct FileInfo *file_list = (struct FileInfo *)image_info->file_list;
955
956   return file_list[graphic].token;
957 #else
958   struct FileInfo *file_list = getImageListEntry(graphic);
959
960   return (file_list != NULL ? file_list->token : NULL);
961 #endif
962 }
963
964 int getImageIDFromToken(char *token)
965 {
966   struct FileInfo *file_list = image_info->file_list;
967   int num_list_entries = image_info->num_file_list_entries;
968   int i;
969
970   for (i = 0; i < num_list_entries; i++)
971     if (strcmp(file_list[i].token, token) == 0)
972       return i;
973
974   return -1;
975 }
976
977 char *getImageConfigFilename()
978 {
979   return getCustomArtworkConfigFilename(image_info->type);
980 }
981
982 int getImageListPropertyMappingSize()
983 {
984   return image_info->num_property_mapping_entries;
985 }
986
987 struct PropertyMapping *getImageListPropertyMapping()
988 {
989   return image_info->property_mapping;
990 }
991
992 void InitImageList(struct ConfigInfo *config_list, int num_file_list_entries,
993                    struct ConfigTypeInfo *config_suffix_list,
994                    char **base_prefixes, char **ext1_suffixes,
995                    char **ext2_suffixes, char **ext3_suffixes,
996                    char **ignore_tokens)
997 {
998   int i;
999
1000   image_info = checked_calloc(sizeof(struct ArtworkListInfo));
1001   image_info->type = ARTWORK_TYPE_GRAPHICS;
1002
1003   /* ---------- initialize file list and suffix lists ---------- */
1004
1005   image_info->num_file_list_entries = num_file_list_entries;
1006   image_info->num_dynamic_file_list_entries = 0;
1007
1008   image_info->file_list =
1009     getFileListFromConfigList(config_list, config_suffix_list, ignore_tokens,
1010                               num_file_list_entries);
1011   image_info->dynamic_file_list = NULL;
1012
1013   image_info->num_suffix_list_entries = 0;
1014   for (i = 0; config_suffix_list[i].token != NULL; i++)
1015     image_info->num_suffix_list_entries++;
1016
1017   image_info->suffix_list = config_suffix_list;
1018
1019   /* ---------- initialize base prefix and suffixes lists ---------- */
1020
1021   image_info->num_base_prefixes = 0;
1022   for (i = 0; base_prefixes[i] != NULL; i++)
1023     image_info->num_base_prefixes++;
1024
1025   image_info->num_ext1_suffixes = 0;
1026   for (i = 0; ext1_suffixes[i] != NULL; i++)
1027     image_info->num_ext1_suffixes++;
1028
1029   image_info->num_ext2_suffixes = 0;
1030   for (i = 0; ext2_suffixes[i] != NULL; i++)
1031     image_info->num_ext2_suffixes++;
1032
1033   image_info->num_ext3_suffixes = 0;
1034   for (i = 0; ext3_suffixes[i] != NULL; i++)
1035     image_info->num_ext3_suffixes++;
1036
1037   image_info->num_ignore_tokens = 0;
1038   for (i = 0; ignore_tokens[i] != NULL; i++)
1039     image_info->num_ignore_tokens++;
1040
1041   image_info->base_prefixes = base_prefixes;
1042   image_info->ext1_suffixes = ext1_suffixes;
1043   image_info->ext2_suffixes = ext2_suffixes;
1044   image_info->ext3_suffixes = ext3_suffixes;
1045   image_info->ignore_tokens = ignore_tokens;
1046
1047   image_info->num_property_mapping_entries = 0;
1048
1049   image_info->property_mapping = NULL;
1050
1051   /* ---------- initialize artwork reference and content lists ---------- */
1052
1053   image_info->sizeof_artwork_list_entry = sizeof(ImageInfo *);
1054
1055   image_info->artwork_list =
1056     checked_calloc(num_file_list_entries * sizeof(ImageInfo *));
1057   image_info->dynamic_artwork_list = NULL;
1058
1059   image_info->content_list = NULL;
1060
1061   /* ---------- initialize artwork loading/freeing functions ---------- */
1062
1063   image_info->load_artwork = Load_PCX;
1064   image_info->free_artwork = FreeImage;
1065 }
1066
1067 void ReloadCustomImages()
1068 {
1069 #if 0
1070   printf("DEBUG: reloading images '%s' ...\n", artwork.gfx_current_identifier);
1071 #endif
1072
1073   LoadArtworkConfig(image_info);
1074   ReloadCustomArtworkList(image_info);
1075 }
1076
1077 void CreateImageWithSmallImages(int pos)
1078 {
1079   ImageInfo *img_info = getImageInfoEntryFromImageID(pos);
1080
1081   if (img_info == NULL || img_info->contains_small_images)
1082     return;
1083
1084   CreateBitmapWithSmallBitmaps(img_info->bitmap);
1085
1086   img_info->contains_small_images = TRUE;
1087
1088 #if 0
1089   printf("CreateImageWithSmallImages: '%s' done\n", img_info->source_filename);
1090 #endif
1091 }
1092
1093 void FreeAllImages()
1094 {
1095   FreeCustomArtworkLists(image_info);
1096 }