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