rnd-20001203-5-src
[rocksndiamonds.git] / src / libgame / image.c
1 /***********************************************************
2 *  Rocks'n'Diamonds -- McDuffin Strikes Back!              *
3 *----------------------------------------------------------*
4 *  (c) 1995-98 Artsoft Entertainment                       *
5 *              Holger Schemel                              *
6 *              Oststrasse 11a                              *
7 *              33604 Bielefeld                             *
8 *              phone: ++49 +521 290471                     *
9 *              email: aeglos@valinor.owl.de                *
10 *----------------------------------------------------------*
11 *  image.c                                                 *
12 ***********************************************************/
13
14 #include "image.h"
15 #include "pcx.h"
16 #include "misc.h"
17
18
19 #if defined(TARGET_X11)
20
21 /* for MS-DOS/Allegro, exclude all except newImage() and freeImage() */
22
23 Image *newImage(unsigned int width, unsigned int height, unsigned int depth)
24 {
25   Image *image;
26   const unsigned int bytes_per_pixel = 1;
27   int i;
28
29   if (depth > 8)
30     Error(ERR_EXIT, "images with more than 256 colors are not supported");
31
32   depth = 8;
33   image = checked_malloc(sizeof(Image));
34   image->data = checked_malloc(width * height * bytes_per_pixel);
35   image->width = width;
36   image->height = height;
37   image->depth = depth;
38   image->rgb.used = 0;
39   for (i=0; i<MAX_COLORS; i++)
40     image->rgb.color_used[i] = FALSE;
41
42   return image;
43 }
44
45 void freeImage(Image *image)
46 {
47   free(image->data);
48   free(image);
49 }
50
51 #if defined(PLATFORM_UNIX)
52
53 /* extra colors to try allocating in private color maps to minimize flashing */
54 #define NOFLASH_COLORS 256
55
56 /* architecture independent value-to-memory conversion
57    note: the internal format is big endian */
58
59 #define value_to_memory(value, ptr, length) (                           \
60 (length) == 1 ? (*( (byte *)(ptr)   ) = ( value     ) ) :               \
61 (length) == 2 ? (*( (byte *)(ptr)   ) = (((unsigned long)(value))>> 8), \
62                  *(((byte *)(ptr))+1) = ( value     ) ) :               \
63 (length) == 3 ? (*( (byte *)(ptr)   ) = (((unsigned long)(value))>>16), \
64                  *(((byte *)(ptr))+1) = (((unsigned long)(value))>> 8), \
65                  *(((byte *)(ptr))+2) = ( value     ) ) :               \
66                 (*( (byte *)(ptr)   ) = (((unsigned long)(value))>>24), \
67                  *(((byte *)(ptr))+1) = (((unsigned long)(value))>>16), \
68                  *(((byte *)(ptr))+2) = (((unsigned long)(value))>> 8), \
69                  *(((byte *)(ptr))+3) = ( value     ) ))
70
71 static Pixmap Image_to_Mask(Image *image, Display *display, Window window)
72 {
73   byte *src_ptr, *dst_ptr, *dst_ptr2;
74   unsigned int bytes_per_row;
75   unsigned int x, y;
76   byte bitmask;
77   byte *mask_data;
78   Pixmap mask_pixmap;
79
80   bytes_per_row = (image->width + 7) / 8;
81   mask_data = checked_calloc(bytes_per_row * image->height);
82
83   src_ptr = image->data;
84   dst_ptr = mask_data;
85
86   /* create bitmap data which can be used by 'XCreateBitmapFromData()'
87    * directly to create a pixmap of depth 1 for use as a clip mask for
88    * the corresponding image pixmap
89    */
90
91   for (y=0; y<image->height; y++)
92   {
93     bitmask = 0x01;             /* start with leftmost bit in the byte     */
94     dst_ptr2 = dst_ptr;         /* start with leftmost byte in the row     */
95
96     for (x=0; x<image->width; x++)
97     {
98       if (*src_ptr++)           /* source pixel solid? (pixel index != 0)  */
99         *dst_ptr2 |= bitmask;   /* then write a bit into the image mask    */
100
101       if ((bitmask <<= 1) == 0) /* bit at rightmost byte position reached? */
102       {
103         bitmask = 0x01;         /* start again with leftmost bit position  */
104         dst_ptr2++;             /* continue with next byte in image mask   */
105       }
106     }
107
108     dst_ptr += bytes_per_row;   /* continue with leftmost byte of next row */
109   }
110
111   mask_pixmap = XCreateBitmapFromData(display, window, (char *)mask_data,
112                                       image->width, image->height);
113   free(mask_data);
114
115   return mask_pixmap;
116 }
117
118 static int bitsPerPixelAtDepth(Display *display, int screen, int depth)
119 {
120   XPixmapFormatValues *pixmap_format;
121   int i, num_pixmap_formats, bits_per_pixel = -1;
122
123   /* get Pixmap formats supported by the X server */
124   pixmap_format = XListPixmapFormats(display, &num_pixmap_formats);
125
126   /* find format that matches the given depth */
127   for (i=0; i<num_pixmap_formats; i++)
128     if (pixmap_format[i].depth == depth)
129       bits_per_pixel = pixmap_format[i].bits_per_pixel;
130
131   XFree(pixmap_format);
132
133   if (bits_per_pixel == -1)
134     Error(ERR_EXIT, "cannot find pixmap format for depth %d", depth);
135
136   return bits_per_pixel;
137 }
138
139 XImageInfo *Image_to_Pixmap(Display *display, int screen, Visual *visual,
140                             Window window, GC gc, int depth, Image *image)
141 {
142   static XColor xcolor_private[NOFLASH_COLORS];
143   static int colorcell_used[NOFLASH_COLORS];
144   static Colormap global_cmap = 0;
145   static Pixel *global_cmap_index;
146   static int num_cmap_entries, free_cmap_entries;
147   static boolean private_cmap = FALSE;
148   Pixel *redvalue, *greenvalue, *bluevalue;
149   unsigned int a, c = 0, x, y, bytes_per_pixel, bits_per_pixel;
150   XColor xcolor;
151   XImage *ximage;
152   XImageInfo *ximageinfo;
153   byte *src_ptr, *dst_ptr;
154
155   if (!global_cmap)
156   {
157     if (visual == DefaultVisual(display, screen))
158       global_cmap = DefaultColormap(display, screen);
159     else
160     {
161       global_cmap = XCreateColormap(display, RootWindow(display, screen),
162                                     visual, AllocNone);
163       private_cmap = TRUE;
164     }
165   }
166
167   xcolor.flags = DoRed | DoGreen | DoBlue;
168   redvalue = greenvalue = bluevalue = NULL;
169   ximageinfo = checked_malloc(sizeof(XImageInfo));
170   ximageinfo->display = display;
171   ximageinfo->depth = depth;
172
173   switch (visual->class)
174   {
175     case TrueColor:
176     case DirectColor:
177     {
178       Pixel pixval;
179       unsigned int redcolors, greencolors, bluecolors;
180       unsigned int redstep, greenstep, bluestep;
181       unsigned int redbottom, greenbottom, bluebottom;
182       unsigned int redtop, greentop, bluetop;
183
184       redvalue = (Pixel *)checked_malloc(sizeof(Pixel) * 256);
185       greenvalue = (Pixel *)checked_malloc(sizeof(Pixel) * 256);
186       bluevalue = (Pixel *)checked_malloc(sizeof(Pixel) * 256);
187
188       ximageinfo->cmap = global_cmap;
189
190       retry_direct: /* tag we hit if a DirectColor allocation fails on
191                      * default colormap */
192
193       /* calculate number of distinct colors in each band */
194
195       redcolors = greencolors = bluecolors = 1;
196       for (pixval=1; pixval; pixval <<= 1)
197       {
198         if (pixval & visual->red_mask)
199           redcolors <<= 1;
200         if (pixval & visual->green_mask)
201           greencolors <<= 1;
202         if (pixval & visual->blue_mask)
203           bluecolors <<= 1;
204       }
205       
206       /* consistency check */
207       if (redcolors > visual->map_entries ||
208           greencolors > visual->map_entries ||
209           bluecolors > visual->map_entries)
210         Error(ERR_WARN, "inconsistency in color information");
211
212       redstep = 256 / redcolors;
213       greenstep = 256 / greencolors;
214       bluestep = 256 / bluecolors;
215       redbottom = greenbottom = bluebottom = 0;
216       redtop = greentop = bluetop = 0;
217       for (a=0; a<visual->map_entries; a++)
218       {
219         if (redbottom < 256)
220           redtop = redbottom + redstep;
221         if (greenbottom < 256)
222           greentop = greenbottom + greenstep;
223         if (bluebottom < 256)
224           bluetop = bluebottom + bluestep;
225
226         xcolor.red = (redtop - 1) << 8;
227         xcolor.green = (greentop - 1) << 8;
228         xcolor.blue = (bluetop - 1) << 8;
229         if (!XAllocColor(display, ximageinfo->cmap, &xcolor))
230         {
231           /* if an allocation fails for a DirectColor default visual then
232              we should create a private colormap and try again. */
233
234           if ((visual->class == DirectColor) &&
235               (visual == DefaultVisual(display, screen)))
236           {
237             global_cmap = XCopyColormapAndFree(display, global_cmap);
238             ximageinfo->cmap = global_cmap;
239             private_cmap = TRUE;
240
241             goto retry_direct;
242           }
243
244           /* something completely unexpected happened */
245
246           fprintf(stderr, "imageToXImage: XAllocColor failed on a TrueColor/Directcolor visual\n");
247           free(redvalue);
248           free(greenvalue);
249           free(bluevalue);
250           free(ximageinfo);
251           return NULL;
252         }
253
254         /* fill in pixel values for each band at this intensity */
255
256         while ((redbottom < 256) && (redbottom < redtop))
257           redvalue[redbottom++] = xcolor.pixel & visual->red_mask;
258         while ((greenbottom < 256) && (greenbottom < greentop))
259           greenvalue[greenbottom++] = xcolor.pixel & visual->green_mask;
260         while ((bluebottom < 256) && (bluebottom < bluetop))
261           bluevalue[bluebottom++] = xcolor.pixel & visual->blue_mask;
262       }
263       break;
264     }
265
266     case PseudoColor:
267
268       ximageinfo->cmap = global_cmap;
269
270       for (a=0; a<MAX_COLORS; a++)
271       {
272         XColor xcolor2;
273         unsigned short mask;
274         int color_found;
275         int i;
276
277         if (!image->rgb.color_used[a])
278           continue;
279
280         xcolor.red = *(image->rgb.red + a);
281         xcolor.green = *(image->rgb.green + a);
282         xcolor.blue = *(image->rgb.blue + a);
283   
284         /* look if this color already exists in our colormap */
285         if (!XAllocColor(display, ximageinfo->cmap, &xcolor))
286         {
287           if (!private_cmap)
288           {
289             if (options.verbose)
290               Error(ERR_RETURN, "switching to private colormap");
291
292             /* we just filled up the default colormap -- get a private one
293                which contains all already allocated colors */
294
295             global_cmap = XCopyColormapAndFree(display, global_cmap);
296             ximageinfo->cmap = global_cmap;
297             private_cmap = TRUE;
298
299             /* allocate the rest of the color cells read/write */
300             global_cmap_index =
301               (Pixel *)checked_malloc(sizeof(Pixel) * NOFLASH_COLORS);
302             for (i=0; i<NOFLASH_COLORS; i++)
303               if (!XAllocColorCells(display, global_cmap, FALSE, NULL, 0,
304                                     global_cmap_index + i, 1))
305                 break;
306             num_cmap_entries = free_cmap_entries = i;
307
308             /*
309             printf("We've got %d free colormap entries.\n", free_cmap_entries);
310             */
311
312             /* to minimize colormap flashing, copy default colors and try
313                to keep them as near as possible to the old values */
314
315             for(i=0; i<num_cmap_entries; i++)
316             {
317               xcolor2.pixel = *(global_cmap_index + i);
318               XQueryColor(display, DefaultColormap(display, screen), &xcolor2);
319               XStoreColor(display, global_cmap, &xcolor2);
320               xcolor_private[xcolor2.pixel] = xcolor2;
321               colorcell_used[xcolor2.pixel] = FALSE;
322             }
323
324             /* now we have the default colormap private: all colors we
325                successfully allocated so far are read-only, which is okay,
326                because we don't want to change them anymore -- if we need
327                an existing color again, we get it by XAllocColor; all other
328                colors are read/write and we can set them by XStoreColor,
329                but we will try to overwrite those color cells with our new
330                color which are as close as possible to our new color */
331           }
332
333           /* look for an existing default color close the one we want */
334
335           mask = 0xf000;
336           color_found = FALSE;
337
338           while (!color_found)
339           {
340             for (i=num_cmap_entries-1; i>=0; i--)
341             {
342               xcolor2.pixel = *(global_cmap_index + i);
343               xcolor2 = xcolor_private[xcolor2.pixel];
344
345               if (colorcell_used[xcolor2.pixel])
346                 continue;
347
348               if ((xcolor.red & mask) == (xcolor2.red & mask) &&
349                   (xcolor.green & mask) == (xcolor2.green & mask) &&
350                   (xcolor.blue & mask) == (xcolor2.blue & mask))
351               {
352                 /*
353                 printf("replacing color cell %ld with a close color\n",
354                        xcolor2.pixel);
355                        */
356                 color_found = TRUE;
357                 break;
358               }
359             }
360
361             if (mask == 0x0000)
362               break;
363
364             mask = (mask << 1) & 0xffff;
365           }
366
367           if (!color_found)             /* no more free color cells */
368             Error(ERR_EXIT, "cannot allocate enough color cells");
369
370           xcolor.pixel = xcolor2.pixel;
371           xcolor_private[xcolor.pixel] = xcolor;
372           colorcell_used[xcolor.pixel] = TRUE;
373           XStoreColor(display, ximageinfo->cmap, &xcolor);
374           free_cmap_entries--;
375         }
376
377         *(ximageinfo->index + a) = xcolor.pixel;
378       }
379
380       /*
381       printf("still %d free colormap entries\n", free_cmap_entries);
382       */
383
384       ximageinfo->no = a;       /* number of pixels allocated for this image */
385       break;
386   
387     default:
388       Error(ERR_RETURN, "display class not supported");
389       Error(ERR_EXIT, "DirectColor, TrueColor or PseudoColor display needed");
390       break;
391   }
392
393 #if DEBUG_TIMING
394   debug_print_timestamp(2, "   ALLOCATING IMAGE COLORS:   ");
395 #endif
396
397   /* create XImage from internal image structure and convert it to Pixmap */
398
399   bits_per_pixel = bitsPerPixelAtDepth(display, screen, depth);
400   bytes_per_pixel = (bits_per_pixel + 7) / 8;
401
402   ximage = XCreateImage(display, visual, depth, ZPixmap, 0,
403                         NULL, image->width, image->height,
404                         8, image->width * bytes_per_pixel);
405   ximage->data =
406     checked_malloc(image->width * image->height * bytes_per_pixel);
407   ximage->byte_order = MSBFirst;
408
409   src_ptr = image->data;
410   dst_ptr = (byte *)ximage->data;
411
412   switch (visual->class)
413   {
414     case DirectColor:
415     case TrueColor:
416     {
417       Pixel pixval;
418
419       for (y=0; y<image->height; y++)           /* general case */
420       {
421         for (x=0; x<image->width; x++)
422         {
423           pixval = *src_ptr++;
424           pixval =
425             redvalue[image->rgb.red[pixval] >> 8] |
426             greenvalue[image->rgb.green[pixval] >> 8] |
427             bluevalue[image->rgb.blue[pixval] >> 8];
428           value_to_memory(pixval, dst_ptr, bytes_per_pixel);
429           dst_ptr += bytes_per_pixel;
430         }
431       }
432       break;
433     }
434
435     case PseudoColor:
436     {
437       if (bytes_per_pixel == 1)                 /* (common) special case */
438       {
439         for (y=0; y<image->height; y++)
440           for (x=0; x<image->width; x++)
441             *dst_ptr++ = ximageinfo->index[c + *src_ptr++];
442       }
443       else                                      /* general case */
444       {
445         for (y=0; y<image->height; y++)
446         {
447           for (x=0; x<image->width; x++)
448           {
449             value_to_memory(ximageinfo->index[c + *src_ptr++],
450                             dst_ptr, bytes_per_pixel);
451             dst_ptr += bytes_per_pixel;
452           }
453         }
454       }
455       break;
456     }
457
458     default:
459       Error(ERR_RETURN, "display class not supported");
460       Error(ERR_EXIT, "DirectColor, TrueColor or PseudoColor display needed");
461       break;
462   }
463
464   if (redvalue)
465   {
466     free((byte *)redvalue);
467     free((byte *)greenvalue);
468     free((byte *)bluevalue);
469   }
470
471 #if DEBUG_TIMING
472   debug_print_timestamp(2, "   CONVERTING IMAGE TO XIMAGE:");
473 #endif
474
475   ximageinfo->pixmap = XCreatePixmap(display, window,
476                                      ximage->width, ximage->height,
477                                      ximageinfo->depth);
478
479   XPutImage(ximageinfo->display, ximageinfo->pixmap, gc,
480             ximage, 0, 0, 0, 0, ximage->width, ximage->height);
481
482   free(ximage->data);
483   ximage->data = NULL;
484   XDestroyImage(ximage);
485
486   return(ximageinfo);
487 }
488
489 void freeXImage(Image *image, XImageInfo *ximageinfo)
490 {
491   if (ximageinfo->index != NULL && ximageinfo->no > 0)
492     XFreeColors(ximageinfo->display, ximageinfo->cmap, ximageinfo->index,
493                 ximageinfo->no, 0);
494   /* this       ^^^^^^^^^^^^^^ is wrong, because the used color cells
495    * are somewhere between 0 and MAX_COLORS; there are indeed 'ximageinfo->no'
496    * used color cells, but they are not at array position 0 - 'ximageinfo->no'
497    */
498
499   free(ximageinfo);
500 }
501
502 int Read_PCX_to_Pixmap(Display *display, Window window, GC gc, char *filename,
503                        Pixmap *pixmap, Pixmap *pixmap_mask)
504 {
505   Image *image;
506   XImageInfo *ximageinfo;
507   int screen;
508   Visual *visual;
509   int depth;
510
511 #if DEBUG_TIMING
512   debug_print_timestamp(2, NULL);       /* initialize timestamp function */
513 #endif
514
515   /* read the graphic file in PCX format to image structure */
516   if ((image = Read_PCX_to_Image(filename)) == NULL)
517     return errno_pcx;
518
519 #if DEBUG_TIMING
520   printf("%s:\n", filename);
521   debug_print_timestamp(2, "   READING PCX FILE TO IMAGE: ");
522 #endif
523
524   screen = DefaultScreen(display);
525   visual = DefaultVisual(display, screen);
526   depth = DefaultDepth(display, screen);
527
528   /* convert image structure to X11 Pixmap */
529   if (!(ximageinfo = Image_to_Pixmap(display, screen, visual,
530                                      window, gc, depth, image)))
531     Error(ERR_EXIT, "cannot convert Image to Pixmap");
532
533   /* if a private colormap has been created, install it */
534   if (ximageinfo->cmap != DefaultColormap(display, screen))
535     XSetWindowColormap(display, window, ximageinfo->cmap);
536
537 #if DEBUG_TIMING
538   debug_print_timestamp(2, "   CONVERTING IMAGE TO PIXMAP:");
539 #endif
540
541   /* create clip mask for the image */
542   ximageinfo->pixmap_mask = Image_to_Mask(image, display, window);
543
544 #if DEBUG_TIMING
545   debug_print_timestamp(2, "   CONVERTING IMAGE TO MASK:  ");
546 #endif
547
548   *pixmap = ximageinfo->pixmap;
549   *pixmap_mask = ximageinfo->pixmap_mask;
550
551   return PCX_Success;
552 }
553
554 #endif  /* PLATFORM_UNIX */
555 #endif  /* TARGET_X11 */