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