1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-98 Artsoft Entertainment *
8 * phone: ++49 +521 290471 *
9 * email: aeglos@valinor.owl.de *
10 *----------------------------------------------------------*
12 ***********************************************************/
29 /* extra colors to try allocating in private color maps to minimize flashing */
30 #define NOFLASH_COLORS 256
32 /* architecture independent value-to-memory conversion
33 note: the internal format is big endian */
35 #define value_to_memory(value, ptr, length) ( \
36 (length) == 1 ? (*( (byte *)(ptr) ) = ( value ) ) : \
37 (length) == 2 ? (*( (byte *)(ptr) ) = (((unsigned long)(value))>> 8), \
38 *(((byte *)(ptr))+1) = ( value ) ) : \
39 (length) == 3 ? (*( (byte *)(ptr) ) = (((unsigned long)(value))>>16), \
40 *(((byte *)(ptr))+1) = (((unsigned long)(value))>> 8), \
41 *(((byte *)(ptr))+2) = ( value ) ) : \
42 (*( (byte *)(ptr) ) = (((unsigned long)(value))>>24), \
43 *(((byte *)(ptr))+1) = (((unsigned long)(value))>>16), \
44 *(((byte *)(ptr))+2) = (((unsigned long)(value))>> 8), \
45 *(((byte *)(ptr))+3) = ( value ) ))
47 static Pixmap Image_to_Mask(Image *image, Display *display, Window window)
49 unsigned char *src_ptr, *dst_ptr, *dst_ptr2;
50 unsigned int bytes_per_row;
52 unsigned char bitmask;
56 bytes_per_row = (image->width + 7) / 8;
57 mask_data = checked_calloc(bytes_per_row * image->height);
59 src_ptr = image->data;
62 /* create bitmap data which can be used by 'XCreateBitmapFromData()'
63 * directly to create a pixmap of depth 1 for use as a clip mask for
64 * the corresponding image pixmap
67 for (y=0; y<image->height; y++)
69 bitmask = 0x01; /* start with leftmost bit in the byte */
70 dst_ptr2 = dst_ptr; /* start with leftmost byte in the row */
72 for (x=0; x<image->width; x++)
74 if (*src_ptr++) /* source pixel solid? (pixel index != 0) */
75 *dst_ptr2 |= bitmask; /* then write a bit into the image mask */
77 if ((bitmask <<= 1) == 0) /* bit at rightmost byte position reached? */
79 bitmask = 0x01; /* start again with leftmost bit position */
80 dst_ptr2++; /* continue with next byte in image mask */
84 dst_ptr += bytes_per_row; /* continue with leftmost byte of next row */
87 mask_pixmap = XCreateBitmapFromData(display, window, mask_data,
88 image->width, image->height);
94 /* find the best pixmap depth supported by the server for a particular
95 visual and return that depth */
97 static unsigned int bitsPerPixelAtDepth(Display *display, int screen,
100 XPixmapFormatValues *xf;
103 xf = XListPixmapFormats(display, &nxf);
104 for (a = 0; a < nxf; a++)
106 if (xf[a].depth == depth)
109 bpp = xf[a].bits_per_pixel;
111 return (unsigned int) bpp;
116 /* this should never happen; if it does, we're in trouble */
117 Error(ERR_EXIT, "bitsPerPixelAtDepth: can't find pixmap depth info");
118 return 0; /* never reached -- just to make gcc happy */
121 XImageInfo *Image_to_Pixmap(Display *display, int screen, Visual *visual,
122 Window window, GC gc,
123 unsigned int depth, Image *image)
125 static XColor xcolor_private[NOFLASH_COLORS];
126 static int colorcell_used[NOFLASH_COLORS];
127 static Colormap global_cmap = 0;
128 static Pixel *global_cmap_index;
129 static int num_cmap_entries, free_cmap_entries;
130 static boolean private_cmap = FALSE;
131 Pixel *redvalue, *greenvalue, *bluevalue;
132 unsigned int a, c = 0, x, y, bytes_per_pixel, bits_per_pixel;
135 XImageInfo *ximageinfo;
136 byte *src_ptr, *dst_ptr;
140 if (visual == DefaultVisual(display, screen))
141 global_cmap = DefaultColormap(display, screen);
144 global_cmap = XCreateColormap(display, RootWindow(display, screen),
150 xcolor.flags = DoRed | DoGreen | DoBlue;
151 redvalue = greenvalue = bluevalue = NULL;
152 ximageinfo = checked_malloc(sizeof(XImageInfo));
153 ximageinfo->display = display;
154 ximageinfo->screen = screen;
155 ximageinfo->depth = depth;
157 switch (visual->class)
163 unsigned int redcolors, greencolors, bluecolors;
164 unsigned int redstep, greenstep, bluestep;
165 unsigned int redbottom, greenbottom, bluebottom;
166 unsigned int redtop, greentop, bluetop;
168 redvalue = (Pixel *)checked_malloc(sizeof(Pixel) * 256);
169 greenvalue = (Pixel *)checked_malloc(sizeof(Pixel) * 256);
170 bluevalue = (Pixel *)checked_malloc(sizeof(Pixel) * 256);
172 ximageinfo->cmap = global_cmap;
174 retry_direct: /* tag we hit if a DirectColor allocation fails on
175 * default colormap */
177 /* calculate number of distinct colors in each band */
179 redcolors = greencolors = bluecolors = 1;
180 for (pixval=1; pixval; pixval <<= 1)
182 if (pixval & visual->red_mask)
184 if (pixval & visual->green_mask)
186 if (pixval & visual->blue_mask)
190 /* consistency check */
191 if (redcolors > visual->map_entries ||
192 greencolors > visual->map_entries ||
193 bluecolors > visual->map_entries)
194 Error(ERR_WARN, "inconsistency in color information");
196 redstep = 256 / redcolors;
197 greenstep = 256 / greencolors;
198 bluestep = 256 / bluecolors;
199 redbottom = greenbottom = bluebottom = 0;
200 redtop = greentop = bluetop = 0;
201 for (a=0; a<visual->map_entries; a++)
204 redtop = redbottom + redstep;
205 if (greenbottom < 256)
206 greentop = greenbottom + greenstep;
207 if (bluebottom < 256)
208 bluetop = bluebottom + bluestep;
210 xcolor.red = (redtop - 1) << 8;
211 xcolor.green = (greentop - 1) << 8;
212 xcolor.blue = (bluetop - 1) << 8;
213 if (!XAllocColor(display, ximageinfo->cmap, &xcolor))
215 /* if an allocation fails for a DirectColor default visual then
216 we should create a private colormap and try again. */
218 if ((visual->class == DirectColor) &&
219 (visual == DefaultVisual(display, screen)))
221 global_cmap = XCopyColormapAndFree(display, global_cmap);
222 ximageinfo->cmap = global_cmap;
228 /* something completely unexpected happened */
230 fprintf(stderr, "imageToXImage: XAllocColor failed on a TrueColor/Directcolor visual\n");
238 /* fill in pixel values for each band at this intensity */
240 while ((redbottom < 256) && (redbottom < redtop))
241 redvalue[redbottom++] = xcolor.pixel & visual->red_mask;
242 while ((greenbottom < 256) && (greenbottom < greentop))
243 greenvalue[greenbottom++] = xcolor.pixel & visual->green_mask;
244 while ((bluebottom < 256) && (bluebottom < bluetop))
245 bluevalue[bluebottom++] = xcolor.pixel & visual->blue_mask;
252 ximageinfo->cmap = global_cmap;
254 for (a=0; a<MAX_COLORS; a++)
261 if (!image->rgb.color_used[a])
264 xcolor.red = *(image->rgb.red + a);
265 xcolor.green = *(image->rgb.green + a);
266 xcolor.blue = *(image->rgb.blue + a);
268 /* look if this color already exists in our colormap */
269 if (!XAllocColor(display, ximageinfo->cmap, &xcolor))
274 Error(ERR_RETURN, "switching to private colormap");
276 /* we just filled up the default colormap -- get a private one
277 which contains all already allocated colors */
279 global_cmap = XCopyColormapAndFree(display, global_cmap);
280 ximageinfo->cmap = global_cmap;
283 /* allocate the rest of the color cells read/write */
285 (Pixel *)checked_malloc(sizeof(Pixel) * NOFLASH_COLORS);
286 for (i=0; i<NOFLASH_COLORS; i++)
287 if (!XAllocColorCells(display, global_cmap, FALSE, NULL, 0,
288 global_cmap_index + i, 1))
290 num_cmap_entries = free_cmap_entries = i;
293 printf("We've got %d free colormap entries.\n", free_cmap_entries);
296 /* to minimize colormap flashing, copy default colors and try
297 to keep them as near as possible to the old values */
299 for(i=0; i<num_cmap_entries; i++)
301 xcolor2.pixel = *(global_cmap_index + i);
302 XQueryColor(display, DefaultColormap(display, screen), &xcolor2);
303 XStoreColor(display, global_cmap, &xcolor2);
304 xcolor_private[xcolor2.pixel] = xcolor2;
305 colorcell_used[xcolor2.pixel] = FALSE;
308 /* now we have the default colormap private: all colors we
309 successfully allocated so far are read-only, which is okay,
310 because we don't want to change them anymore -- if we need
311 an existing color again, we get it by XAllocColor; all other
312 colors are read/write and we can set them by XStoreColor,
313 but we will try to overwrite those color cells with our new
314 color which are as close as possible to our new color */
317 /* look for an existing default color close the one we want */
324 for (i=num_cmap_entries-1; i>=0; i--)
326 xcolor2.pixel = *(global_cmap_index + i);
327 xcolor2 = xcolor_private[xcolor2.pixel];
329 if (colorcell_used[xcolor2.pixel])
332 if ((xcolor.red & mask) == (xcolor2.red & mask) &&
333 (xcolor.green & mask) == (xcolor2.green & mask) &&
334 (xcolor.blue & mask) == (xcolor2.blue & mask))
337 printf("replacing color cell %ld with a close color\n",
348 mask = (mask << 1) & 0xffff;
351 if (!color_found) /* no more free color cells */
352 Error(ERR_EXIT, "cannot allocate enough color cells");
354 xcolor.pixel = xcolor2.pixel;
355 xcolor_private[xcolor.pixel] = xcolor;
356 colorcell_used[xcolor.pixel] = TRUE;
357 XStoreColor(display, ximageinfo->cmap, &xcolor);
361 *(ximageinfo->index + a) = xcolor.pixel;
365 printf("still %d free colormap entries\n", free_cmap_entries);
368 ximageinfo->no = a; /* number of pixels allocated for this image */
372 Error(ERR_RETURN, "display type not supported");
373 Error(ERR_EXIT, "DirectColor, TrueColor or PseudoColor display needed");
379 printf(" CONVERTING IMAGE TO XIMAGE (IMAGE COLORMAP) IN %.2f SECONDS\n",
380 (float)(count2-count1)/1000.0);
384 /* create XImage from internal image structure and convert it to Pixmap */
386 bits_per_pixel = bitsPerPixelAtDepth(display, screen, depth);
387 bytes_per_pixel = (bits_per_pixel + 7) / 8;
389 ximage = XCreateImage(display, visual, depth, ZPixmap, 0,
390 NULL, image->width, image->height,
391 8, image->width * bytes_per_pixel);
393 checked_malloc(image->width * image->height * bytes_per_pixel);
394 ximage->byte_order = MSBFirst;
396 src_ptr = image->data;
397 dst_ptr = ximage->data;
399 switch (visual->class)
406 for (y=0; y<image->height; y++) /* general case */
408 for (x=0; x<image->width; x++)
412 redvalue[image->rgb.red[pixval] >> 8] |
413 greenvalue[image->rgb.green[pixval] >> 8] |
414 bluevalue[image->rgb.blue[pixval] >> 8];
415 value_to_memory(pixval, dst_ptr, bytes_per_pixel);
416 dst_ptr += bytes_per_pixel;
424 if (bytes_per_pixel == 1) /* (common) special case */
426 for (y=0; y<image->height; y++)
427 for (x=0; x<image->width; x++)
428 *dst_ptr++ = ximageinfo->index[c + *src_ptr++];
430 else /* general case */
432 for (y=0; y<image->height; y++)
434 for (x=0; x<image->width; x++)
436 value_to_memory(ximageinfo->index[c + *src_ptr++],
437 dst_ptr, bytes_per_pixel);
438 dst_ptr += bytes_per_pixel;
446 Error(ERR_RETURN, "display type not supported");
447 Error(ERR_EXIT, "DirectColor, TrueColor or PseudoColor display needed");
453 free((byte *)redvalue);
454 free((byte *)greenvalue);
455 free((byte *)bluevalue);
460 printf(" CONVERTING IMAGE TO XIMAGE IN %.2f SECONDS\n",
461 (float)(count2-count1)/1000.0);
465 ximageinfo->pixmap = XCreatePixmap(display, window,
466 ximage->width, ximage->height,
469 XPutImage(ximageinfo->display, ximageinfo->pixmap, gc,
470 ximage, 0, 0, 0, 0, ximage->width, ximage->height);
474 XDestroyImage(ximage);
479 void freeXImage(Image *image, XImageInfo *ximageinfo)
481 if (ximageinfo->index != NULL && ximageinfo->no > 0)
482 XFreeColors(ximageinfo->display, ximageinfo->cmap, ximageinfo->index,
484 /* this ^^^^^^^^^^^^^^ is wrong, because the used color cells
485 * are somewhere between 0 and MAX_COLORS; there are indeed 'ximageinfo->no'
486 * used color cells, but they are not at array position 0 - 'ximageinfo->no'
492 Image *newImage(unsigned int width, unsigned int height, unsigned int depth)
495 const unsigned int bytes_per_pixel = 1;
499 Error(ERR_EXIT, "images with more than 256 colors are not supported");
502 image = checked_malloc(sizeof(Image));
503 image->data = checked_malloc(width * height * bytes_per_pixel);
504 image->width = width;
505 image->height = height;
506 image->depth = depth;
508 for (i=0; i<MAX_COLORS; i++)
509 image->rgb.color_used[i] = FALSE;
514 void freeImage(Image *image)
520 int Read_PCX_to_Pixmaps(Display *display, Window window, GC gc, char *filename,
521 Pixmap *pixmap, Pixmap *pixmap_mask)
524 XImageInfo *ximageinfo;
533 /* read the graphic file in PCX format to image structure */
534 if ((image = Read_PCX_to_Image(filename)) == NULL)
535 return PCX_FileInvalid;
539 printf(" LOADING '%s' IN %.2f SECONDS\n",
540 filename, (float)(count2-count1)/1000.0);
544 screen = DefaultScreen(display);
545 visual = DefaultVisual(display, screen);
546 depth = DefaultDepth(display, screen);
548 /* convert image structure to X11 Pixmap */
549 if (!(ximageinfo = Image_to_Pixmap(display, screen, visual,
550 window, gc, depth, image)))
551 Error(ERR_EXIT, "cannot convert Image to Pixmap");
553 /* if a private colormap has been created, install it */
554 if (ximageinfo->cmap != DefaultColormap(display, screen))
555 XSetWindowColormap(display, window, ximageinfo->cmap);
559 printf(" CONVERTING IMAGE TO PIXMAP IN %.2f SECONDS\n",
560 (float)(count2-count1)/1000.0);
564 /* create clip mask for the image */
565 ximageinfo->pixmap_mask = Image_to_Mask(image, display, window);
569 printf(" CONVERTING IMAGE TO MASK IN %.2f SECONDS\n",
570 (float)(count2-count1)/1000.0);
574 *pixmap = ximageinfo->pixmap;
575 *pixmap_mask = ximageinfo->pixmap_mask;