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