rnd-19981113-2
[rocksndiamonds.git] / src / 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 "misc.h"
16
17
18 #ifdef DEBUG
19
20 #define DEBUG_TIMING
21
22 #endif
23
24 #ifdef DEBUG_TIMING
25   long count1, count2;
26 #endif
27
28
29 /* extra colors to try allocating in private color maps to minimise flashing */
30 #define NOFLASH_COLORS 256
31
32 static void Image_to_Mask(Image *image)
33 {
34   unsigned char *src_ptr, *dst_ptr, *dst_ptr2;
35   unsigned int bytes_per_row;
36   unsigned int x, y;
37   unsigned char bitmask;
38
39   bytes_per_row = (image->width + 7) / 8;
40   src_ptr = image->data;
41   dst_ptr = image->data_mask;
42
43   for (y=0; y<image->height; y++)
44   {
45     bitmask = 0x80;             /* start with leftmost bit in the byte     */
46     dst_ptr2 = dst_ptr;         /* start with leftmost byte in the row     */
47
48     for (x=0; x<image->width; x++)
49     {
50       if (*src_ptr++)           /* source pixel solid? (pixel index != 0)  */
51         *dst_ptr2 |= bitmask;   /* then write a bit into the image mask    */
52
53       if ((bitmask >>= 1) == 0) /* bit at rightmost byte position reached? */
54       {
55         bitmask = 0x80;         /* start again with leftmost bit position  */
56         dst_ptr2++;             /* continue with next byte in image mask   */
57       }
58     }
59
60     dst_ptr += bytes_per_row;   /* continue with leftmost byte of next row */
61   }
62 }
63
64 static boolean XImage_to_Pixmap(Display *display, Window parent,
65                                 XImageInfo *ximageinfo)
66 {
67   XGCValues gcv;
68
69   ximageinfo->pixmap =
70     XCreatePixmap(display, parent,
71                   ximageinfo->ximage->width,
72                   ximageinfo->ximage->height,
73                   ximageinfo->depth);
74
75   ximageinfo->pixmap_mask =
76     XCreatePixmap(display, parent,
77                   ximageinfo->ximage->width,
78                   ximageinfo->ximage->height,
79                   1);
80
81   /* build and cache the GC */
82
83   if (!ximageinfo->gc)
84   {
85     gcv.function = GXcopy;
86     ximageinfo->gc =
87       XCreateGC(ximageinfo->display, ximageinfo->pixmap,
88                 GCFunction, &gcv);
89   }
90
91   if (!ximageinfo->gc_mask)
92   {
93     gcv.function = GXcopy;
94     gcv.foreground = ximageinfo->foreground;
95     gcv.background = ximageinfo->background;
96     ximageinfo->gc_mask =
97       XCreateGC(ximageinfo->display, ximageinfo->pixmap_mask,
98                 GCFunction | GCForeground | GCBackground, &gcv);
99   }
100
101   XPutImage(ximageinfo->display, ximageinfo->pixmap, ximageinfo->gc,
102             ximageinfo->ximage, 0, 0, 0, 0,
103             ximageinfo->ximage->width, ximageinfo->ximage->height);
104   XPutImage(ximageinfo->display, ximageinfo->pixmap_mask, ximageinfo->gc_mask,
105             ximageinfo->ximage_mask, 0, 0, 0, 0,
106             ximageinfo->ximage->width, ximageinfo->ximage->height);
107
108   return (ximageinfo->pixmap != None && ximageinfo->pixmap_mask != None);
109 }
110
111 /* find the best pixmap depth supported by the server for a particular
112  * visual and return that depth.
113  */
114
115 static unsigned int bitsPerPixelAtDepth(Display *display, int screen,
116                                         unsigned int depth)
117 {
118   XPixmapFormatValues *xf;
119   int nxf, a;
120
121   xf = XListPixmapFormats(display, &nxf);
122   for (a = 0; a < nxf; a++)
123   {
124     if (xf[a].depth == depth)
125     {
126       int bpp;
127       bpp = xf[a].bits_per_pixel;
128       XFree(xf);
129       return (unsigned int) bpp;
130     }
131   }
132   XFree(xf);
133
134   /* this should never happen; if it does, we're in trouble */
135
136   fprintf(stderr, "bitsPerPixelAtDepth: Can't find pixmap depth info!\n");
137   exit(1);
138 }
139
140 XImageInfo *Image_to_XImage(Display *display, int screen, Visual *visual,
141                             unsigned int ddepth, Image *image)
142 {
143   static XColor xcolor_private[NOFLASH_COLORS];
144   static int colorcell_used[NOFLASH_COLORS];
145   static Colormap global_cmap = 0;
146   static Pixel *global_cmap_index;
147   static int num_cmap_entries, free_cmap_entries;
148   static boolean private_cmap = FALSE;
149   Pixel *redvalue, *greenvalue, *bluevalue;
150   unsigned int a, c=0, x, y, linelen, dpixlen, dbits;
151   XColor xcolor;
152   XGCValues gcv;
153   XImageInfo *ximageinfo;
154
155   /* for building image */
156   byte *data, *destptr, *srcptr;
157
158   /* for building image mask */
159   byte *data_mask;
160
161   if (!global_cmap)
162   {
163     if (visual == DefaultVisual(display, screen))
164       global_cmap = DefaultColormap(display, screen);
165     else
166     {
167       global_cmap = XCreateColormap(display, RootWindow(display, screen),
168                                          visual, AllocNone);
169       private_cmap = TRUE;
170     }
171   }
172
173   xcolor.flags = DoRed | DoGreen | DoBlue;
174   redvalue = greenvalue = bluevalue = NULL;
175   ximageinfo = (XImageInfo *)checked_malloc(sizeof(XImageInfo));
176   ximageinfo->display = display;
177   ximageinfo->screen = screen;
178   ximageinfo->depth = 0;
179   ximageinfo->drawable = None;
180   ximageinfo->index = NULL;
181   ximageinfo->rootimage = FALSE;
182   ximageinfo->foreground = ximageinfo->background= 0;
183   ximageinfo->gc = NULL;
184   ximageinfo->gc_mask = NULL;
185   ximageinfo->ximage = NULL;
186   ximageinfo->ximage_mask = NULL;
187
188   switch (visual->class)
189   {
190     case TrueColor:
191     case DirectColor:
192     {
193       Pixel pixval;
194       unsigned int redcolors, greencolors, bluecolors;
195       unsigned int redstep, greenstep, bluestep;
196       unsigned int redbottom, greenbottom, bluebottom;
197       unsigned int redtop, greentop, bluetop;
198
199       redvalue = (Pixel *)checked_malloc(sizeof(Pixel) * 256);
200       greenvalue = (Pixel *)checked_malloc(sizeof(Pixel) * 256);
201       bluevalue = (Pixel *)checked_malloc(sizeof(Pixel) * 256);
202
203       ximageinfo->cmap = global_cmap;
204
205       retry_direct: /* tag we hit if a DirectColor allocation fails on
206                      * default colormap */
207
208       /* calculate number of distinct colors in each band
209        */
210
211       redcolors = greencolors = bluecolors = 1;
212       for (pixval=1; pixval; pixval <<= 1)
213       {
214         if (pixval & visual->red_mask)
215           redcolors <<= 1;
216         if (pixval & visual->green_mask)
217           greencolors <<= 1;
218         if (pixval & visual->blue_mask)
219           bluecolors <<= 1;
220       }
221       
222       /* sanity check
223        */
224
225       if ((redcolors > visual->map_entries) ||
226           (greencolors > visual->map_entries) ||
227           (bluecolors > visual->map_entries))
228       {
229         fprintf(stderr, "Warning: inconsistency in color information (this may be ugly)\n");
230       }
231
232       redstep = 256 / redcolors;
233       greenstep = 256 / greencolors;
234       bluestep = 256 / bluecolors;
235       redbottom = greenbottom = bluebottom = 0;
236       redtop = greentop = bluetop = 0;
237       for (a=0; a<visual->map_entries; a++)
238       {
239         if (redbottom < 256)
240           redtop = redbottom + redstep;
241         if (greenbottom < 256)
242           greentop = greenbottom + greenstep;
243         if (bluebottom < 256)
244           bluetop = bluebottom + bluestep;
245
246         xcolor.red = (redtop - 1) << 8;
247         xcolor.green = (greentop - 1) << 8;
248         xcolor.blue = (bluetop - 1) << 8;
249         if (!XAllocColor(display, ximageinfo->cmap, &xcolor))
250         {
251           /* if an allocation fails for a DirectColor default visual then
252            * we should create a private colormap and try again.
253            */
254
255           if ((visual->class == DirectColor) &&
256               (visual == DefaultVisual(display, screen)))
257           {
258             global_cmap = XCopyColormapAndFree(display, global_cmap);
259             ximageinfo->cmap = global_cmap;
260             private_cmap = TRUE;
261
262             goto retry_direct;
263           }
264
265           /* something completely unexpected happened
266            */
267
268           fprintf(stderr, "imageToXImage: XAllocColor failed on a TrueColor/Directcolor visual\n");
269           free((byte *)redvalue);
270           free((byte *)greenvalue);
271           free((byte *)bluevalue);
272           free((byte *)ximageinfo);
273           return(NULL);
274         }
275
276         /* fill in pixel values for each band at this intensity
277          */
278
279         while ((redbottom < 256) && (redbottom < redtop))
280           redvalue[redbottom++] = xcolor.pixel & visual->red_mask;
281         while ((greenbottom < 256) && (greenbottom < greentop))
282           greenvalue[greenbottom++] = xcolor.pixel & visual->green_mask;
283         while ((bluebottom < 256) && (bluebottom < bluetop))
284           bluevalue[bluebottom++] = xcolor.pixel & visual->blue_mask;
285       }
286       break;
287     }
288
289     case PseudoColor:
290
291       ximageinfo->cmap = global_cmap;
292       ximageinfo->index =
293         (Pixel *)checked_malloc(sizeof(Pixel) * image->rgb.used);
294
295 #if 0
296       for (a=0; a<image->rgb.used; a++)
297 #endif
298
299       for (a=0; a<MAX_COLORS; a++)
300       {
301         XColor xcolor2;
302         unsigned short mask;
303         int color_found;
304         int i;
305
306         if (!image->rgb.color_used[a])
307           continue;
308
309         xcolor.red = *(image->rgb.red + a);
310         xcolor.green = *(image->rgb.green + a);
311         xcolor.blue = *(image->rgb.blue + a);
312   
313         /* look if this color already exists in our colormap */
314         if (!XAllocColor(display, ximageinfo->cmap, &xcolor))
315         {
316           if (!private_cmap)
317           {
318             if (options.verbose)
319               Error(ERR_RETURN, "switching to private colormap");
320
321             /* we just filled up the default colormap -- get a private one
322                which contains all already allocated colors */
323
324             global_cmap = XCopyColormapAndFree(display, global_cmap);
325             ximageinfo->cmap = global_cmap;
326             private_cmap = TRUE;
327
328             /* allocate the rest of the color cells read/write */
329             global_cmap_index =
330               (Pixel *)checked_malloc(sizeof(Pixel) * NOFLASH_COLORS);
331             for (i=0; i<NOFLASH_COLORS; i++)
332               if (!XAllocColorCells(display, global_cmap, FALSE, NULL, 0,
333                                     global_cmap_index + i, 1))
334                 break;
335             num_cmap_entries = free_cmap_entries = i;
336
337             /*
338             printf("We've got %d free colormap entries.\n", free_cmap_entries);
339             */
340
341             /* to minimize colormap flashing, copy default colors and try
342                to keep them as near as possible to the old values */
343
344             for(i=0; i<num_cmap_entries; i++)
345             {
346               xcolor2.pixel = *(global_cmap_index + i);
347               XQueryColor(display, DefaultColormap(display, screen), &xcolor2);
348               XStoreColor(display, global_cmap, &xcolor2);
349               xcolor_private[xcolor2.pixel] = xcolor2;
350               colorcell_used[xcolor2.pixel] = FALSE;
351             }
352
353             /* now we have the default colormap private: all colors we
354                successfully allocated so far are read-only, which is okay,
355                because we don't want to change them anymore -- if we need
356                an existing color again, we get it by XAllocColor; all other
357                colors are read/write and we can set them by XStoreColor,
358                but we will try to overwrite those color cells with our new
359                color which are as close as possible to our new color */
360           }
361
362           /* look for an existing default color close the one we want */
363
364           mask = 0xf000;
365           color_found = FALSE;
366
367           while (!color_found)
368           {
369             for (i=num_cmap_entries-1; i>=0; i--)
370             {
371               xcolor2.pixel = *(global_cmap_index + i);
372               xcolor2 = xcolor_private[xcolor2.pixel];
373
374               if (colorcell_used[xcolor2.pixel])
375                 continue;
376
377               if ((xcolor.red & mask) == (xcolor2.red & mask) &&
378                   (xcolor.green & mask) == (xcolor2.green & mask) &&
379                   (xcolor.blue & mask) == (xcolor2.blue & mask))
380               {
381                 /*
382                 printf("replacing color cell %ld with a close color\n",
383                        xcolor2.pixel);
384                        */
385                 color_found = TRUE;
386                 break;
387               }
388             }
389
390             if (mask == 0x0000)
391               break;
392
393             mask = (mask << 1) & 0xffff;
394           }
395
396           if (!color_found)             /* no more free color cells */
397           {
398             printf("Sorry, cannot allocate enough colors!\n");
399             exit(0);
400           }
401
402           xcolor.pixel = xcolor2.pixel;
403           xcolor_private[xcolor.pixel] = xcolor;
404           colorcell_used[xcolor.pixel] = TRUE;
405           XStoreColor(display, ximageinfo->cmap, &xcolor);
406           free_cmap_entries--;
407         }
408
409         *(ximageinfo->index + a) = xcolor.pixel;
410       }
411
412       /*
413       printf("still %d free colormap entries\n", free_cmap_entries);
414       */
415
416       ximageinfo->no = a;       /* number of pixels allocated for this image */
417       break;
418   
419     default:
420       printf("Sorry, only DirectColor, TrueColor and PseudoColor supported\n");
421       exit(0);
422       break;
423   }
424
425 #ifdef DEBUG_TIMING
426   count2 = Counter();
427   printf("   CONVERTING IMAGE TO XIMAGE (COLORMAP) IN %.2f SECONDS\n",
428          (float)(count2-count1)/1000.0);
429   count1 = Counter();
430 #endif
431
432   /* CREATE IMAGE ITSELF */
433   /* modify image data to match visual and colormap */
434
435   dbits = bitsPerPixelAtDepth(display, screen, ddepth);/* bits per pixel */
436   dpixlen = (dbits + 7) / 8;                    /* bytes per pixel */
437
438   ximageinfo->ximage = XCreateImage(display, visual, ddepth, ZPixmap, 0,
439                                     NULL, image->width, image->height,
440                                     8, image->width * dpixlen);
441
442   data = (byte *)checked_malloc(image->width * image->height * dpixlen);
443   ximageinfo->depth = ddepth;
444   ximageinfo->ximage->data = (char *)data;
445   ximageinfo->ximage->byte_order = MSBFirst;
446   srcptr = image->data;
447   destptr = data;
448
449   switch (visual->class)
450   {
451     case DirectColor:
452     case TrueColor:
453     {
454       Pixel pixval;
455
456       for (y=0; y<image->height; y++)
457       {
458         for (x=0; x<image->width; x++)
459         {
460           pixval = memToVal(srcptr, 1);
461           pixval =
462             redvalue[image->rgb.red[pixval] >> 8] |
463             greenvalue[image->rgb.green[pixval] >> 8] |
464             bluevalue[image->rgb.blue[pixval] >> 8];
465           valToMem(pixval, destptr, dpixlen);
466           srcptr += 1;
467           destptr += dpixlen;
468         }
469       }
470       break;
471     }
472
473     default:
474     {
475       if (dpixlen == 1)                 /* most common */
476       {
477         for (y=0; y<image->height; y++)
478         {
479           for (x=0; x<image->width; x++)
480           {
481             *destptr = ximageinfo->index[c + *srcptr];
482             srcptr++;
483             destptr++;
484           }
485         }
486       }
487       else                                      /* less common */
488       {
489         for (y=0; y<image->height; y++)
490         {
491           for (x=0; x<image->width; x++)
492           {
493             register unsigned long temp;
494             temp = memToVal(srcptr, 1);
495             valToMem(ximageinfo->index[c + temp], destptr, dpixlen);
496             srcptr += 1;
497             destptr += dpixlen;
498           }
499         }
500       }
501     }
502   }
503
504   /* NOW CREATE IMAGE MASK */
505   /* we copy the data to be more consistent */
506
507   linelen = ((image->width + 7) / 8);
508   data_mask = checked_malloc(linelen * image->height);
509
510   memcpy((char *)data_mask, (char *)image->data_mask,
511          linelen * image->height);
512
513   gcv.function = GXcopy;
514   ximageinfo->ximage_mask =
515     XCreateImage(display, visual, 1, XYBitmap, 0, (char *)data_mask,
516                  image->width, image->height, 8, linelen);
517
518 #if 1
519   if (visual->class == DirectColor || visual->class == TrueColor)
520   {
521     Pixel pixval;
522     dbits = bitsPerPixelAtDepth(display, screen, ddepth);
523     dpixlen = (dbits + 7) / 8;
524     pixval =
525       redvalue[65535 >> 8] |
526       greenvalue[65535 >> 8] |
527       bluevalue[65535 >> 8];
528     ximageinfo->background = pixval;
529     pixval =
530       redvalue[0 >> 8] |
531       greenvalue[0 >> 8] |
532       bluevalue[0 >> 8];
533     ximageinfo->foreground = pixval;
534   }
535   else  /* Not Direct or True Color */
536   {
537     ximageinfo->foreground = BlackPixel(display, screen);
538     ximageinfo->background = WhitePixel(display, screen);
539   }
540 #else
541   ximageinfo->foreground = WhitePixel(display, screen);
542   ximageinfo->background = BlackPixel(display, screen);
543 #endif
544
545   ximageinfo->ximage_mask->bitmap_bit_order = MSBFirst;
546   ximageinfo->ximage_mask->byte_order = MSBFirst;
547
548   if (redvalue)
549   {
550     free((byte *)redvalue);
551     free((byte *)greenvalue);
552     free((byte *)bluevalue);
553   }
554
555   return(ximageinfo);
556 }
557
558 /* free up anything cached in the local Ximage structure.
559  */
560
561 void freeXImage(Image *image, XImageInfo *ximageinfo)
562 {
563   if (ximageinfo->index != NULL)        /* if we allocated colors */
564   {
565     if (ximageinfo->no > 0 && !ximageinfo->rootimage)   /* don't free root colors */
566       XFreeColors(ximageinfo->display, ximageinfo->cmap, ximageinfo->index,
567                   ximageinfo->no, 0);
568     free(ximageinfo->index);
569   }
570   if (ximageinfo->gc)
571     XFreeGC(ximageinfo->display, ximageinfo->gc);
572   free((byte *)ximageinfo->ximage->data);
573   ximageinfo->ximage->data= NULL;
574   XDestroyImage(ximageinfo->ximage);
575   free((byte *)ximageinfo);
576   /* should we free private color map to ??? */
577 }
578
579 Image *newImage(unsigned int width, unsigned int height, unsigned int depth)
580 {
581   Image *image;
582   unsigned int bytes_per_row;
583   const unsigned int bytes_per_pixel = 1;
584   int i;
585
586   if (depth > 8)
587     Error(ERR_EXIT, "images with more than 256 colors are not supported");
588
589   depth = 8;
590   image = checked_malloc(sizeof(Image));
591   image->data = checked_malloc(width * height * bytes_per_pixel);
592   image->width = width;
593   image->height = height;
594   image->depth = depth;
595   image->rgb.used = 0;
596   for (i=0; i<MAX_COLORS; i++)
597     image->rgb.color_used[i] = FALSE;
598
599   bytes_per_row = (width + 7) / 8;
600   image->data_mask = checked_calloc(bytes_per_row * height);
601
602   return image;
603 }
604
605 void freeImage(Image *image)
606 {
607   free(image->data);
608   free(image->data_mask);
609   free(image);
610 }
611
612 /* ------------------------------------------------------------------------- */
613
614
615
616 int Read_PCX_to_Pixmaps(Display *display, Window window, char *filename,
617                         Pixmap *pixmap, Pixmap *pixmap_mask)
618 {
619   Image *image;
620   XImageInfo *ximageinfo;
621
622   /*
623   Image *image, *image_mask;
624   XImageInfo *ximageinfo, *ximageinfo_mask;
625   */
626
627   int screen;
628   Visual *visual;
629   unsigned int depth;
630
631 #ifdef DEBUG_TIMING
632   count1 = Counter();
633 #endif
634
635   if ((image = Read_PCX_to_Image(filename)) == NULL)
636     return PCX_FileInvalid;
637
638 #ifdef DEBUG_TIMING
639   count2 = Counter();
640   printf("   LOADING '%s' IN %.2f SECONDS\n",
641          filename, (float)(count2-count1)/1000.0);
642   count1 = Counter();
643 #endif
644
645   /* create image mask */
646   Image_to_Mask(image);
647
648 #ifdef DEBUG_TIMING
649   count2 = Counter();
650   printf("   CONVERTING IMAGE TO MASK IN %.2f SECONDS\n",
651          (float)(count2-count1)/1000.0);
652   count1 = Counter();
653 #endif
654
655   screen = DefaultScreen(display);
656   visual = DefaultVisual(display, screen);
657   depth = DefaultDepth(display, screen);
658
659   /* convert internal image structure to X11 XImage */
660   if (!(ximageinfo = Image_to_XImage(display, screen, visual, depth, image)))
661   {
662     fprintf(stderr, "Cannot convert Image to XImage.\n");
663     exit(1);
664   }
665
666 #ifdef DEBUG_TIMING
667   count2 = Counter();
668   printf("   CONVERTING IMAGE TO XIMAGE (BITMAP) IN %.2f SECONDS\n",
669          (float)(count2-count1)/1000.0);
670   count1 = Counter();
671 #endif
672
673   if (ximageinfo->cmap != DefaultColormap(display, screen))
674     XSetWindowColormap(display, window, ximageinfo->cmap);
675
676   /* convert XImage to Pixmap */
677   if (!(XImage_to_Pixmap(display, window, ximageinfo)))
678   {
679     fprintf(stderr, "Cannot convert XImage to Pixmap.\n");
680     exit(1);
681   }
682
683 #ifdef DEBUG_TIMING
684   count2 = Counter();
685   printf("   CONVERTING IMAGE TO PIXMAP IN %.2f SECONDS\n",
686          (float)(count2-count1)/1000.0);
687   count1 = Counter();
688 #endif
689
690   *pixmap = ximageinfo->pixmap;
691   *pixmap_mask = ximageinfo->pixmap_mask;
692
693   return(PCX_Success);
694 }