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