rnd-20131212-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 int)(                *( (byte *)(ptr))         ) : \
68 (len) == 2 ? (unsigned int)(((unsigned int)(*( (byte *)(ptr))   ))<< 8)   \
69                          + (                *(((byte *)(ptr))+1)      ) : \
70 (len) == 3 ? (unsigned int)(((unsigned int)(*( (byte *)(ptr))   ))<<16)   \
71                          + (((unsigned int)(*(((byte *)(ptr))+1)))<< 8)   \
72                          + (                *(((byte *)(ptr))+2)      ) : \
73              (unsigned int)(((unsigned int)(*( (byte *)(ptr))   ))<<24)   \
74                          + (((unsigned int)(*(((byte *)(ptr))+1)))<<16)   \
75                          + (((unsigned int)(*(((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 int)(value))>> 8),     \
82               *(((byte *)(ptr))+1) = ( value     ) ) :                  \
83 (len) == 3 ? (*( (byte *)(ptr)   ) = (((unsigned int)(value))>>16),     \
84               *(((byte *)(ptr))+1) = (((unsigned int)(value))>> 8),     \
85               *(((byte *)(ptr))+2) = ( value     ) ) :                  \
86              (*( (byte *)(ptr)   ) = (((unsigned int)(value))>>24),     \
87               *(((byte *)(ptr))+1) = (((unsigned int)(value))>>16),     \
88               *(((byte *)(ptr))+2) = (((unsigned int)(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 #if 1
836 static void *Load_Image(char *filename)
837 #else
838 static void *Load_PCX(char *filename)
839 #endif
840 {
841   ImageInfo *img_info;
842
843 #if 0
844   printf("::: loading PCX file '%s'\n", filename);
845 #endif
846
847   img_info = checked_calloc(sizeof(ImageInfo));
848
849   if ((img_info->bitmap = LoadImage(filename)) == NULL)
850   {
851     Error(ERR_WARN, "cannot load image file '%s': LoadImage() failed: %s",
852           filename, GetError());
853     free(img_info);
854     return NULL;
855   }
856
857   img_info->source_filename = getStringCopy(filename);
858
859   img_info->original_width  = img_info->bitmap->width;
860   img_info->original_height = img_info->bitmap->height;
861
862   img_info->contains_small_images = FALSE;
863   img_info->scaled_up = FALSE;
864
865   return img_info;
866 }
867
868 static void FreeImage(void *ptr)
869 {
870   ImageInfo *image = (ImageInfo *)ptr;
871
872   if (image == NULL)
873     return;
874
875   if (image->bitmap)
876     FreeBitmap(image->bitmap);
877
878   if (image->source_filename)
879     free(image->source_filename);
880
881   free(image);
882 }
883
884 int getImageListSize()
885 {
886   return (image_info->num_file_list_entries +
887           image_info->num_dynamic_file_list_entries);
888 }
889
890 struct FileInfo *getImageListEntryFromImageID(int pos)
891 {
892   int num_list_entries = image_info->num_file_list_entries;
893   int list_pos = (pos < num_list_entries ? pos : pos - num_list_entries);
894
895   return (pos < num_list_entries ? &image_info->file_list[list_pos] :
896           &image_info->dynamic_file_list[list_pos]);
897 }
898
899 static ImageInfo *getImageInfoEntryFromImageID(int pos)
900 {
901   int num_list_entries = image_info->num_file_list_entries;
902   int list_pos = (pos < num_list_entries ? pos : pos - num_list_entries);
903   ImageInfo **img_info =
904     (ImageInfo **)(pos < num_list_entries ? image_info->artwork_list :
905                    image_info->dynamic_artwork_list);
906
907   return img_info[list_pos];
908 }
909
910 Bitmap *getBitmapFromImageID(int pos)
911 {
912   ImageInfo *img_info = getImageInfoEntryFromImageID(pos);
913
914   return (img_info != NULL ? img_info->bitmap : NULL);
915 }
916
917 int getOriginalImageWidthFromImageID(int pos)
918 {
919   ImageInfo *img_info = getImageInfoEntryFromImageID(pos);
920
921   return (img_info != NULL ? img_info->original_width : 0);
922 }
923
924 int getOriginalImageHeightFromImageID(int pos)
925 {
926   ImageInfo *img_info = getImageInfoEntryFromImageID(pos);
927
928   return (img_info != NULL ? img_info->original_height : 0);
929 }
930
931 char *getTokenFromImageID(int graphic)
932 {
933   struct FileInfo *file_list = getImageListEntryFromImageID(graphic);
934
935   return (file_list != NULL ? file_list->token : NULL);
936 }
937
938 int getImageIDFromToken(char *token)
939 {
940   struct FileInfo *file_list = image_info->file_list;
941   int num_list_entries = image_info->num_file_list_entries;
942   int i;
943
944   for (i = 0; i < num_list_entries; i++)
945     if (strEqual(file_list[i].token, token))
946       return i;
947
948   return -1;
949 }
950
951 char *getImageConfigFilename()
952 {
953   return getCustomArtworkConfigFilename(image_info->type);
954 }
955
956 int getImageListPropertyMappingSize()
957 {
958   return image_info->num_property_mapping_entries;
959 }
960
961 struct PropertyMapping *getImageListPropertyMapping()
962 {
963   return image_info->property_mapping;
964 }
965
966 void InitImageList(struct ConfigInfo *config_list, int num_file_list_entries,
967                    struct ConfigTypeInfo *config_suffix_list,
968                    char **base_prefixes, char **ext1_suffixes,
969                    char **ext2_suffixes, char **ext3_suffixes,
970                    char **ignore_tokens)
971 {
972   int i;
973
974   image_info = checked_calloc(sizeof(struct ArtworkListInfo));
975   image_info->type = ARTWORK_TYPE_GRAPHICS;
976
977   /* ---------- initialize file list and suffix lists ---------- */
978
979   image_info->num_file_list_entries = num_file_list_entries;
980   image_info->num_dynamic_file_list_entries = 0;
981
982   image_info->file_list =
983     getFileListFromConfigList(config_list, config_suffix_list, ignore_tokens,
984                               num_file_list_entries);
985   image_info->dynamic_file_list = NULL;
986
987   image_info->num_suffix_list_entries = 0;
988   for (i = 0; config_suffix_list[i].token != NULL; i++)
989     image_info->num_suffix_list_entries++;
990
991   image_info->suffix_list = config_suffix_list;
992
993   /* ---------- initialize base prefix and suffixes lists ---------- */
994
995   image_info->num_base_prefixes = 0;
996   for (i = 0; base_prefixes[i] != NULL; i++)
997     image_info->num_base_prefixes++;
998
999   image_info->num_ext1_suffixes = 0;
1000   for (i = 0; ext1_suffixes[i] != NULL; i++)
1001     image_info->num_ext1_suffixes++;
1002
1003   image_info->num_ext2_suffixes = 0;
1004   for (i = 0; ext2_suffixes[i] != NULL; i++)
1005     image_info->num_ext2_suffixes++;
1006
1007   image_info->num_ext3_suffixes = 0;
1008   for (i = 0; ext3_suffixes[i] != NULL; i++)
1009     image_info->num_ext3_suffixes++;
1010
1011   image_info->num_ignore_tokens = 0;
1012   for (i = 0; ignore_tokens[i] != NULL; i++)
1013     image_info->num_ignore_tokens++;
1014
1015   image_info->base_prefixes = base_prefixes;
1016   image_info->ext1_suffixes = ext1_suffixes;
1017   image_info->ext2_suffixes = ext2_suffixes;
1018   image_info->ext3_suffixes = ext3_suffixes;
1019   image_info->ignore_tokens = ignore_tokens;
1020
1021   image_info->num_property_mapping_entries = 0;
1022
1023   image_info->property_mapping = NULL;
1024
1025   /* ---------- initialize artwork reference and content lists ---------- */
1026
1027   image_info->sizeof_artwork_list_entry = sizeof(ImageInfo *);
1028
1029   image_info->artwork_list =
1030     checked_calloc(num_file_list_entries * sizeof(ImageInfo *));
1031   image_info->dynamic_artwork_list = NULL;
1032
1033   image_info->content_list = NULL;
1034
1035   /* ---------- initialize artwork loading/freeing functions ---------- */
1036
1037 #if 1
1038   image_info->load_artwork = Load_Image;
1039 #else
1040   image_info->load_artwork = Load_PCX;
1041 #endif
1042   image_info->free_artwork = FreeImage;
1043 }
1044
1045 void ReloadCustomImages()
1046 {
1047 #if 0
1048   printf("::: reloading images '%s' ...\n", artwork.gfx_current_identifier);
1049 #endif
1050
1051   print_timestamp_init("ReloadCustomImages");
1052
1053   LoadArtworkConfig(image_info);
1054   print_timestamp_time("LoadArtworkConfig");
1055
1056   ReloadCustomArtworkList(image_info);
1057   print_timestamp_time("ReloadCustomArtworkList");
1058
1059   print_timestamp_done("ReloadCustomImages");
1060 }
1061
1062 void CreateImageWithSmallImages(int pos, int zoom_factor)
1063 {
1064   ImageInfo *img_info = getImageInfoEntryFromImageID(pos);
1065
1066   if (img_info == NULL || img_info->contains_small_images)
1067     return;
1068
1069   CreateBitmapWithSmallBitmaps(img_info->bitmap, zoom_factor);
1070
1071   img_info->contains_small_images = TRUE;
1072   img_info->scaled_up = TRUE;
1073 }
1074
1075 void ScaleImage(int pos, int zoom_factor)
1076 {
1077   ImageInfo *img_info = getImageInfoEntryFromImageID(pos);
1078
1079   if (img_info == NULL || img_info->scaled_up)
1080     return;
1081
1082   if (zoom_factor != 1)
1083     ScaleBitmap(img_info->bitmap, zoom_factor);
1084
1085   img_info->scaled_up = TRUE;
1086 }
1087
1088 void FreeAllImages()
1089 {
1090   FreeCustomArtworkLists(image_info);
1091 }