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