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 ***********************************************************/
17 /* exclude all except newImage() and freeImage() */
20 /* extra colors to try allocating in private color maps to minimize flashing */
21 #define NOFLASH_COLORS 256
23 /* architecture independent value-to-memory conversion
24 note: the internal format is big endian */
26 #define value_to_memory(value, ptr, length) ( \
27 (length) == 1 ? (*( (byte *)(ptr) ) = ( value ) ) : \
28 (length) == 2 ? (*( (byte *)(ptr) ) = (((unsigned long)(value))>> 8), \
29 *(((byte *)(ptr))+1) = ( value ) ) : \
30 (length) == 3 ? (*( (byte *)(ptr) ) = (((unsigned long)(value))>>16), \
31 *(((byte *)(ptr))+1) = (((unsigned long)(value))>> 8), \
32 *(((byte *)(ptr))+2) = ( value ) ) : \
33 (*( (byte *)(ptr) ) = (((unsigned long)(value))>>24), \
34 *(((byte *)(ptr))+1) = (((unsigned long)(value))>>16), \
35 *(((byte *)(ptr))+2) = (((unsigned long)(value))>> 8), \
36 *(((byte *)(ptr))+3) = ( value ) ))
38 static Pixmap Image_to_Mask(Image *image, Display *display, Window window)
40 byte *src_ptr, *dst_ptr, *dst_ptr2;
41 unsigned int bytes_per_row;
47 bytes_per_row = (image->width + 7) / 8;
48 mask_data = checked_calloc(bytes_per_row * image->height);
50 src_ptr = image->data;
53 /* create bitmap data which can be used by 'XCreateBitmapFromData()'
54 * directly to create a pixmap of depth 1 for use as a clip mask for
55 * the corresponding image pixmap
58 for (y=0; y<image->height; y++)
60 bitmask = 0x01; /* start with leftmost bit in the byte */
61 dst_ptr2 = dst_ptr; /* start with leftmost byte in the row */
63 for (x=0; x<image->width; x++)
65 if (*src_ptr++) /* source pixel solid? (pixel index != 0) */
66 *dst_ptr2 |= bitmask; /* then write a bit into the image mask */
68 if ((bitmask <<= 1) == 0) /* bit at rightmost byte position reached? */
70 bitmask = 0x01; /* start again with leftmost bit position */
71 dst_ptr2++; /* continue with next byte in image mask */
75 dst_ptr += bytes_per_row; /* continue with leftmost byte of next row */
78 mask_pixmap = XCreateBitmapFromData(display, window, mask_data,
79 image->width, image->height);
85 static int bitsPerPixelAtDepth(Display *display, int screen, int depth)
87 XPixmapFormatValues *pixmap_format;
88 int i, num_pixmap_formats, bits_per_pixel = -1;
90 /* get Pixmap formats supported by the X server */
91 pixmap_format = XListPixmapFormats(display, &num_pixmap_formats);
93 /* find format that matches the given depth */
94 for (i=0; i<num_pixmap_formats; i++)
95 if (pixmap_format[i].depth == depth)
96 bits_per_pixel = pixmap_format[i].bits_per_pixel;
100 if (bits_per_pixel == -1)
101 Error(ERR_EXIT, "cannot find pixmap format for depth %d", depth);
103 return bits_per_pixel;
106 XImageInfo *Image_to_Pixmap(Display *display, int screen, Visual *visual,
107 Window window, GC gc, int depth, Image *image)
109 static XColor xcolor_private[NOFLASH_COLORS];
110 static int colorcell_used[NOFLASH_COLORS];
111 static Colormap global_cmap = 0;
112 static Pixel *global_cmap_index;
113 static int num_cmap_entries, free_cmap_entries;
114 static boolean private_cmap = FALSE;
115 Pixel *redvalue, *greenvalue, *bluevalue;
116 unsigned int a, c = 0, x, y, bytes_per_pixel, bits_per_pixel;
119 XImageInfo *ximageinfo;
120 byte *src_ptr, *dst_ptr;
124 if (visual == DefaultVisual(display, screen))
125 global_cmap = DefaultColormap(display, screen);
128 global_cmap = XCreateColormap(display, RootWindow(display, screen),
134 xcolor.flags = DoRed | DoGreen | DoBlue;
135 redvalue = greenvalue = bluevalue = NULL;
136 ximageinfo = checked_malloc(sizeof(XImageInfo));
137 ximageinfo->display = display;
138 ximageinfo->depth = depth;
140 switch (visual->class)
146 unsigned int redcolors, greencolors, bluecolors;
147 unsigned int redstep, greenstep, bluestep;
148 unsigned int redbottom, greenbottom, bluebottom;
149 unsigned int redtop, greentop, bluetop;
151 redvalue = (Pixel *)checked_malloc(sizeof(Pixel) * 256);
152 greenvalue = (Pixel *)checked_malloc(sizeof(Pixel) * 256);
153 bluevalue = (Pixel *)checked_malloc(sizeof(Pixel) * 256);
155 ximageinfo->cmap = global_cmap;
157 retry_direct: /* tag we hit if a DirectColor allocation fails on
158 * default colormap */
160 /* calculate number of distinct colors in each band */
162 redcolors = greencolors = bluecolors = 1;
163 for (pixval=1; pixval; pixval <<= 1)
165 if (pixval & visual->red_mask)
167 if (pixval & visual->green_mask)
169 if (pixval & visual->blue_mask)
173 /* consistency check */
174 if (redcolors > visual->map_entries ||
175 greencolors > visual->map_entries ||
176 bluecolors > visual->map_entries)
177 Error(ERR_WARN, "inconsistency in color information");
179 redstep = 256 / redcolors;
180 greenstep = 256 / greencolors;
181 bluestep = 256 / bluecolors;
182 redbottom = greenbottom = bluebottom = 0;
183 redtop = greentop = bluetop = 0;
184 for (a=0; a<visual->map_entries; a++)
187 redtop = redbottom + redstep;
188 if (greenbottom < 256)
189 greentop = greenbottom + greenstep;
190 if (bluebottom < 256)
191 bluetop = bluebottom + bluestep;
193 xcolor.red = (redtop - 1) << 8;
194 xcolor.green = (greentop - 1) << 8;
195 xcolor.blue = (bluetop - 1) << 8;
196 if (!XAllocColor(display, ximageinfo->cmap, &xcolor))
198 /* if an allocation fails for a DirectColor default visual then
199 we should create a private colormap and try again. */
201 if ((visual->class == DirectColor) &&
202 (visual == DefaultVisual(display, screen)))
204 global_cmap = XCopyColormapAndFree(display, global_cmap);
205 ximageinfo->cmap = global_cmap;
211 /* something completely unexpected happened */
213 fprintf(stderr, "imageToXImage: XAllocColor failed on a TrueColor/Directcolor visual\n");
221 /* fill in pixel values for each band at this intensity */
223 while ((redbottom < 256) && (redbottom < redtop))
224 redvalue[redbottom++] = xcolor.pixel & visual->red_mask;
225 while ((greenbottom < 256) && (greenbottom < greentop))
226 greenvalue[greenbottom++] = xcolor.pixel & visual->green_mask;
227 while ((bluebottom < 256) && (bluebottom < bluetop))
228 bluevalue[bluebottom++] = xcolor.pixel & visual->blue_mask;
235 ximageinfo->cmap = global_cmap;
237 for (a=0; a<MAX_COLORS; a++)
244 if (!image->rgb.color_used[a])
247 xcolor.red = *(image->rgb.red + a);
248 xcolor.green = *(image->rgb.green + a);
249 xcolor.blue = *(image->rgb.blue + a);
251 /* look if this color already exists in our colormap */
252 if (!XAllocColor(display, ximageinfo->cmap, &xcolor))
257 Error(ERR_RETURN, "switching to private colormap");
259 /* we just filled up the default colormap -- get a private one
260 which contains all already allocated colors */
262 global_cmap = XCopyColormapAndFree(display, global_cmap);
263 ximageinfo->cmap = global_cmap;
266 /* allocate the rest of the color cells read/write */
268 (Pixel *)checked_malloc(sizeof(Pixel) * NOFLASH_COLORS);
269 for (i=0; i<NOFLASH_COLORS; i++)
270 if (!XAllocColorCells(display, global_cmap, FALSE, NULL, 0,
271 global_cmap_index + i, 1))
273 num_cmap_entries = free_cmap_entries = i;
276 printf("We've got %d free colormap entries.\n", free_cmap_entries);
279 /* to minimize colormap flashing, copy default colors and try
280 to keep them as near as possible to the old values */
282 for(i=0; i<num_cmap_entries; i++)
284 xcolor2.pixel = *(global_cmap_index + i);
285 XQueryColor(display, DefaultColormap(display, screen), &xcolor2);
286 XStoreColor(display, global_cmap, &xcolor2);
287 xcolor_private[xcolor2.pixel] = xcolor2;
288 colorcell_used[xcolor2.pixel] = FALSE;
291 /* now we have the default colormap private: all colors we
292 successfully allocated so far are read-only, which is okay,
293 because we don't want to change them anymore -- if we need
294 an existing color again, we get it by XAllocColor; all other
295 colors are read/write and we can set them by XStoreColor,
296 but we will try to overwrite those color cells with our new
297 color which are as close as possible to our new color */
300 /* look for an existing default color close the one we want */
307 for (i=num_cmap_entries-1; i>=0; i--)
309 xcolor2.pixel = *(global_cmap_index + i);
310 xcolor2 = xcolor_private[xcolor2.pixel];
312 if (colorcell_used[xcolor2.pixel])
315 if ((xcolor.red & mask) == (xcolor2.red & mask) &&
316 (xcolor.green & mask) == (xcolor2.green & mask) &&
317 (xcolor.blue & mask) == (xcolor2.blue & mask))
320 printf("replacing color cell %ld with a close color\n",
331 mask = (mask << 1) & 0xffff;
334 if (!color_found) /* no more free color cells */
335 Error(ERR_EXIT, "cannot allocate enough color cells");
337 xcolor.pixel = xcolor2.pixel;
338 xcolor_private[xcolor.pixel] = xcolor;
339 colorcell_used[xcolor.pixel] = TRUE;
340 XStoreColor(display, ximageinfo->cmap, &xcolor);
344 *(ximageinfo->index + a) = xcolor.pixel;
348 printf("still %d free colormap entries\n", free_cmap_entries);
351 ximageinfo->no = a; /* number of pixels allocated for this image */
355 Error(ERR_RETURN, "display class not supported");
356 Error(ERR_EXIT, "DirectColor, TrueColor or PseudoColor display needed");
361 debug_print_timestamp(2, " ALLOCATING IMAGE COLORS: ");
364 /* create XImage from internal image structure and convert it to Pixmap */
366 bits_per_pixel = bitsPerPixelAtDepth(display, screen, depth);
367 bytes_per_pixel = (bits_per_pixel + 7) / 8;
369 ximage = XCreateImage(display, visual, depth, ZPixmap, 0,
370 NULL, image->width, image->height,
371 8, image->width * bytes_per_pixel);
373 checked_malloc(image->width * image->height * bytes_per_pixel);
374 ximage->byte_order = MSBFirst;
376 src_ptr = image->data;
377 dst_ptr = ximage->data;
379 switch (visual->class)
386 for (y=0; y<image->height; y++) /* general case */
388 for (x=0; x<image->width; x++)
392 redvalue[image->rgb.red[pixval] >> 8] |
393 greenvalue[image->rgb.green[pixval] >> 8] |
394 bluevalue[image->rgb.blue[pixval] >> 8];
395 value_to_memory(pixval, dst_ptr, bytes_per_pixel);
396 dst_ptr += bytes_per_pixel;
404 if (bytes_per_pixel == 1) /* (common) special case */
406 for (y=0; y<image->height; y++)
407 for (x=0; x<image->width; x++)
408 *dst_ptr++ = ximageinfo->index[c + *src_ptr++];
410 else /* general case */
412 for (y=0; y<image->height; y++)
414 for (x=0; x<image->width; x++)
416 value_to_memory(ximageinfo->index[c + *src_ptr++],
417 dst_ptr, bytes_per_pixel);
418 dst_ptr += bytes_per_pixel;
426 Error(ERR_RETURN, "display class not supported");
427 Error(ERR_EXIT, "DirectColor, TrueColor or PseudoColor display needed");
433 free((byte *)redvalue);
434 free((byte *)greenvalue);
435 free((byte *)bluevalue);
439 debug_print_timestamp(2, " CONVERTING IMAGE TO XIMAGE:");
442 ximageinfo->pixmap = XCreatePixmap(display, window,
443 ximage->width, ximage->height,
446 XPutImage(ximageinfo->display, ximageinfo->pixmap, gc,
447 ximage, 0, 0, 0, 0, ximage->width, ximage->height);
451 XDestroyImage(ximage);
456 void freeXImage(Image *image, XImageInfo *ximageinfo)
458 if (ximageinfo->index != NULL && ximageinfo->no > 0)
459 XFreeColors(ximageinfo->display, ximageinfo->cmap, ximageinfo->index,
461 /* this ^^^^^^^^^^^^^^ is wrong, because the used color cells
462 * are somewhere between 0 and MAX_COLORS; there are indeed 'ximageinfo->no'
463 * used color cells, but they are not at array position 0 - 'ximageinfo->no'
471 Image *newImage(unsigned int width, unsigned int height, unsigned int depth)
474 const unsigned int bytes_per_pixel = 1;
478 Error(ERR_EXIT, "images with more than 256 colors are not supported");
481 image = checked_malloc(sizeof(Image));
482 image->data = checked_malloc(width * height * bytes_per_pixel);
483 image->width = width;
484 image->height = height;
485 image->depth = depth;
487 for (i=0; i<MAX_COLORS; i++)
488 image->rgb.color_used[i] = FALSE;
493 void freeImage(Image *image)
501 int Read_PCX_to_Pixmap(Display *display, Window window, GC gc, char *filename,
502 Pixmap *pixmap, Pixmap *pixmap_mask)
505 XImageInfo *ximageinfo;
511 debug_print_timestamp(2, NULL); /* initialize timestamp function */
514 /* read the graphic file in PCX format to image structure */
515 if ((image = Read_PCX_to_Image(filename)) == NULL)
516 return PCX_FileInvalid;
519 printf("%s:\n", filename);
520 debug_print_timestamp(2, " READING PCX FILE TO IMAGE: ");
523 screen = DefaultScreen(display);
524 visual = DefaultVisual(display, screen);
525 depth = DefaultDepth(display, screen);
527 /* convert image structure to X11 Pixmap */
528 if (!(ximageinfo = Image_to_Pixmap(display, screen, visual,
529 window, gc, depth, image)))
530 Error(ERR_EXIT, "cannot convert Image to Pixmap");
532 /* if a private colormap has been created, install it */
533 if (ximageinfo->cmap != DefaultColormap(display, screen))
534 XSetWindowColormap(display, window, ximageinfo->cmap);
537 debug_print_timestamp(2, " CONVERTING IMAGE TO PIXMAP:");
540 /* create clip mask for the image */
541 ximageinfo->pixmap_mask = Image_to_Mask(image, display, window);
544 debug_print_timestamp(2, " CONVERTING IMAGE TO MASK: ");
547 *pixmap = ximageinfo->pixmap;
548 *pixmap_mask = ximageinfo->pixmap_mask;