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 /* extra colors to try allocating in private color maps to minimize flashing */
18 #define NOFLASH_COLORS 256
20 /* architecture independent value-to-memory conversion
21 note: the internal format is big endian */
23 #define value_to_memory(value, ptr, length) ( \
24 (length) == 1 ? (*( (byte *)(ptr) ) = ( value ) ) : \
25 (length) == 2 ? (*( (byte *)(ptr) ) = (((unsigned long)(value))>> 8), \
26 *(((byte *)(ptr))+1) = ( value ) ) : \
27 (length) == 3 ? (*( (byte *)(ptr) ) = (((unsigned long)(value))>>16), \
28 *(((byte *)(ptr))+1) = (((unsigned long)(value))>> 8), \
29 *(((byte *)(ptr))+2) = ( value ) ) : \
30 (*( (byte *)(ptr) ) = (((unsigned long)(value))>>24), \
31 *(((byte *)(ptr))+1) = (((unsigned long)(value))>>16), \
32 *(((byte *)(ptr))+2) = (((unsigned long)(value))>> 8), \
33 *(((byte *)(ptr))+3) = ( value ) ))
35 static Pixmap Image_to_Mask(Image *image, Display *display, Window window)
37 unsigned char *src_ptr, *dst_ptr, *dst_ptr2;
38 unsigned int bytes_per_row;
40 unsigned char bitmask;
44 bytes_per_row = (image->width + 7) / 8;
45 mask_data = checked_calloc(bytes_per_row * image->height);
47 src_ptr = image->data;
50 /* create bitmap data which can be used by 'XCreateBitmapFromData()'
51 * directly to create a pixmap of depth 1 for use as a clip mask for
52 * the corresponding image pixmap
55 for (y=0; y<image->height; y++)
57 bitmask = 0x01; /* start with leftmost bit in the byte */
58 dst_ptr2 = dst_ptr; /* start with leftmost byte in the row */
60 for (x=0; x<image->width; x++)
62 if (*src_ptr++) /* source pixel solid? (pixel index != 0) */
63 *dst_ptr2 |= bitmask; /* then write a bit into the image mask */
65 if ((bitmask <<= 1) == 0) /* bit at rightmost byte position reached? */
67 bitmask = 0x01; /* start again with leftmost bit position */
68 dst_ptr2++; /* continue with next byte in image mask */
72 dst_ptr += bytes_per_row; /* continue with leftmost byte of next row */
75 mask_pixmap = XCreateBitmapFromData(display, window, mask_data,
76 image->width, image->height);
82 static int bitsPerPixelAtDepth(Display *display, int screen, int depth)
84 XPixmapFormatValues *pixmap_format;
85 int i, num_pixmap_formats, bits_per_pixel = -1;
87 /* get Pixmap formats supported by the X server */
88 pixmap_format = XListPixmapFormats(display, &num_pixmap_formats);
90 /* find format that matches the given depth */
91 for (i=0; i<num_pixmap_formats; i++)
92 if (pixmap_format[i].depth == depth)
93 bits_per_pixel = pixmap_format[i].bits_per_pixel;
97 if (bits_per_pixel == -1)
98 Error(ERR_EXIT, "cannot find pixmap format for depth %d", depth);
100 return bits_per_pixel;
103 XImageInfo *Image_to_Pixmap(Display *display, int screen, Visual *visual,
104 Window window, GC gc, int depth, Image *image)
106 static XColor xcolor_private[NOFLASH_COLORS];
107 static int colorcell_used[NOFLASH_COLORS];
108 static Colormap global_cmap = 0;
109 static Pixel *global_cmap_index;
110 static int num_cmap_entries, free_cmap_entries;
111 static boolean private_cmap = FALSE;
112 Pixel *redvalue, *greenvalue, *bluevalue;
113 unsigned int a, c = 0, x, y, bytes_per_pixel, bits_per_pixel;
116 XImageInfo *ximageinfo;
117 byte *src_ptr, *dst_ptr;
121 if (visual == DefaultVisual(display, screen))
122 global_cmap = DefaultColormap(display, screen);
125 global_cmap = XCreateColormap(display, RootWindow(display, screen),
131 xcolor.flags = DoRed | DoGreen | DoBlue;
132 redvalue = greenvalue = bluevalue = NULL;
133 ximageinfo = checked_malloc(sizeof(XImageInfo));
134 ximageinfo->display = display;
135 ximageinfo->depth = depth;
137 switch (visual->class)
143 unsigned int redcolors, greencolors, bluecolors;
144 unsigned int redstep, greenstep, bluestep;
145 unsigned int redbottom, greenbottom, bluebottom;
146 unsigned int redtop, greentop, bluetop;
148 redvalue = (Pixel *)checked_malloc(sizeof(Pixel) * 256);
149 greenvalue = (Pixel *)checked_malloc(sizeof(Pixel) * 256);
150 bluevalue = (Pixel *)checked_malloc(sizeof(Pixel) * 256);
152 ximageinfo->cmap = global_cmap;
154 retry_direct: /* tag we hit if a DirectColor allocation fails on
155 * default colormap */
157 /* calculate number of distinct colors in each band */
159 redcolors = greencolors = bluecolors = 1;
160 for (pixval=1; pixval; pixval <<= 1)
162 if (pixval & visual->red_mask)
164 if (pixval & visual->green_mask)
166 if (pixval & visual->blue_mask)
170 /* consistency check */
171 if (redcolors > visual->map_entries ||
172 greencolors > visual->map_entries ||
173 bluecolors > visual->map_entries)
174 Error(ERR_WARN, "inconsistency in color information");
176 redstep = 256 / redcolors;
177 greenstep = 256 / greencolors;
178 bluestep = 256 / bluecolors;
179 redbottom = greenbottom = bluebottom = 0;
180 redtop = greentop = bluetop = 0;
181 for (a=0; a<visual->map_entries; a++)
184 redtop = redbottom + redstep;
185 if (greenbottom < 256)
186 greentop = greenbottom + greenstep;
187 if (bluebottom < 256)
188 bluetop = bluebottom + bluestep;
190 xcolor.red = (redtop - 1) << 8;
191 xcolor.green = (greentop - 1) << 8;
192 xcolor.blue = (bluetop - 1) << 8;
193 if (!XAllocColor(display, ximageinfo->cmap, &xcolor))
195 /* if an allocation fails for a DirectColor default visual then
196 we should create a private colormap and try again. */
198 if ((visual->class == DirectColor) &&
199 (visual == DefaultVisual(display, screen)))
201 global_cmap = XCopyColormapAndFree(display, global_cmap);
202 ximageinfo->cmap = global_cmap;
208 /* something completely unexpected happened */
210 fprintf(stderr, "imageToXImage: XAllocColor failed on a TrueColor/Directcolor visual\n");
218 /* fill in pixel values for each band at this intensity */
220 while ((redbottom < 256) && (redbottom < redtop))
221 redvalue[redbottom++] = xcolor.pixel & visual->red_mask;
222 while ((greenbottom < 256) && (greenbottom < greentop))
223 greenvalue[greenbottom++] = xcolor.pixel & visual->green_mask;
224 while ((bluebottom < 256) && (bluebottom < bluetop))
225 bluevalue[bluebottom++] = xcolor.pixel & visual->blue_mask;
232 ximageinfo->cmap = global_cmap;
234 for (a=0; a<MAX_COLORS; a++)
241 if (!image->rgb.color_used[a])
244 xcolor.red = *(image->rgb.red + a);
245 xcolor.green = *(image->rgb.green + a);
246 xcolor.blue = *(image->rgb.blue + a);
248 /* look if this color already exists in our colormap */
249 if (!XAllocColor(display, ximageinfo->cmap, &xcolor))
254 Error(ERR_RETURN, "switching to private colormap");
256 /* we just filled up the default colormap -- get a private one
257 which contains all already allocated colors */
259 global_cmap = XCopyColormapAndFree(display, global_cmap);
260 ximageinfo->cmap = global_cmap;
263 /* allocate the rest of the color cells read/write */
265 (Pixel *)checked_malloc(sizeof(Pixel) * NOFLASH_COLORS);
266 for (i=0; i<NOFLASH_COLORS; i++)
267 if (!XAllocColorCells(display, global_cmap, FALSE, NULL, 0,
268 global_cmap_index + i, 1))
270 num_cmap_entries = free_cmap_entries = i;
273 printf("We've got %d free colormap entries.\n", free_cmap_entries);
276 /* to minimize colormap flashing, copy default colors and try
277 to keep them as near as possible to the old values */
279 for(i=0; i<num_cmap_entries; i++)
281 xcolor2.pixel = *(global_cmap_index + i);
282 XQueryColor(display, DefaultColormap(display, screen), &xcolor2);
283 XStoreColor(display, global_cmap, &xcolor2);
284 xcolor_private[xcolor2.pixel] = xcolor2;
285 colorcell_used[xcolor2.pixel] = FALSE;
288 /* now we have the default colormap private: all colors we
289 successfully allocated so far are read-only, which is okay,
290 because we don't want to change them anymore -- if we need
291 an existing color again, we get it by XAllocColor; all other
292 colors are read/write and we can set them by XStoreColor,
293 but we will try to overwrite those color cells with our new
294 color which are as close as possible to our new color */
297 /* look for an existing default color close the one we want */
304 for (i=num_cmap_entries-1; i>=0; i--)
306 xcolor2.pixel = *(global_cmap_index + i);
307 xcolor2 = xcolor_private[xcolor2.pixel];
309 if (colorcell_used[xcolor2.pixel])
312 if ((xcolor.red & mask) == (xcolor2.red & mask) &&
313 (xcolor.green & mask) == (xcolor2.green & mask) &&
314 (xcolor.blue & mask) == (xcolor2.blue & mask))
317 printf("replacing color cell %ld with a close color\n",
328 mask = (mask << 1) & 0xffff;
331 if (!color_found) /* no more free color cells */
332 Error(ERR_EXIT, "cannot allocate enough color cells");
334 xcolor.pixel = xcolor2.pixel;
335 xcolor_private[xcolor.pixel] = xcolor;
336 colorcell_used[xcolor.pixel] = TRUE;
337 XStoreColor(display, ximageinfo->cmap, &xcolor);
341 *(ximageinfo->index + a) = xcolor.pixel;
345 printf("still %d free colormap entries\n", free_cmap_entries);
348 ximageinfo->no = a; /* number of pixels allocated for this image */
352 Error(ERR_RETURN, "display class not supported");
353 Error(ERR_EXIT, "DirectColor, TrueColor or PseudoColor display needed");
358 debug_print_timestamp(2, " ALLOCATING IMAGE COLORS: ");
361 /* create XImage from internal image structure and convert it to Pixmap */
363 bits_per_pixel = bitsPerPixelAtDepth(display, screen, depth);
364 bytes_per_pixel = (bits_per_pixel + 7) / 8;
366 ximage = XCreateImage(display, visual, depth, ZPixmap, 0,
367 NULL, image->width, image->height,
368 8, image->width * bytes_per_pixel);
370 checked_malloc(image->width * image->height * bytes_per_pixel);
371 ximage->byte_order = MSBFirst;
373 src_ptr = image->data;
374 dst_ptr = ximage->data;
376 switch (visual->class)
383 for (y=0; y<image->height; y++) /* general case */
385 for (x=0; x<image->width; x++)
389 redvalue[image->rgb.red[pixval] >> 8] |
390 greenvalue[image->rgb.green[pixval] >> 8] |
391 bluevalue[image->rgb.blue[pixval] >> 8];
392 value_to_memory(pixval, dst_ptr, bytes_per_pixel);
393 dst_ptr += bytes_per_pixel;
401 if (bytes_per_pixel == 1) /* (common) special case */
403 for (y=0; y<image->height; y++)
404 for (x=0; x<image->width; x++)
405 *dst_ptr++ = ximageinfo->index[c + *src_ptr++];
407 else /* general case */
409 for (y=0; y<image->height; y++)
411 for (x=0; x<image->width; x++)
413 value_to_memory(ximageinfo->index[c + *src_ptr++],
414 dst_ptr, bytes_per_pixel);
415 dst_ptr += bytes_per_pixel;
423 Error(ERR_RETURN, "display class not supported");
424 Error(ERR_EXIT, "DirectColor, TrueColor or PseudoColor display needed");
430 free((byte *)redvalue);
431 free((byte *)greenvalue);
432 free((byte *)bluevalue);
436 debug_print_timestamp(2, " CONVERTING IMAGE TO XIMAGE:");
439 ximageinfo->pixmap = XCreatePixmap(display, window,
440 ximage->width, ximage->height,
443 XPutImage(ximageinfo->display, ximageinfo->pixmap, gc,
444 ximage, 0, 0, 0, 0, ximage->width, ximage->height);
448 XDestroyImage(ximage);
453 void freeXImage(Image *image, XImageInfo *ximageinfo)
455 if (ximageinfo->index != NULL && ximageinfo->no > 0)
456 XFreeColors(ximageinfo->display, ximageinfo->cmap, ximageinfo->index,
458 /* this ^^^^^^^^^^^^^^ is wrong, because the used color cells
459 * are somewhere between 0 and MAX_COLORS; there are indeed 'ximageinfo->no'
460 * used color cells, but they are not at array position 0 - 'ximageinfo->no'
466 Image *newImage(unsigned int width, unsigned int height, unsigned int depth)
469 const unsigned int bytes_per_pixel = 1;
473 Error(ERR_EXIT, "images with more than 256 colors are not supported");
476 image = checked_malloc(sizeof(Image));
477 image->data = checked_malloc(width * height * bytes_per_pixel);
478 image->width = width;
479 image->height = height;
480 image->depth = depth;
482 for (i=0; i<MAX_COLORS; i++)
483 image->rgb.color_used[i] = FALSE;
488 void freeImage(Image *image)
494 int Read_PCX_to_Pixmaps(Display *display, Window window, GC gc, char *filename,
495 Pixmap *pixmap, Pixmap *pixmap_mask)
498 XImageInfo *ximageinfo;
504 debug_print_timestamp(2, NULL); /* initialize timestamp function */
507 /* read the graphic file in PCX format to image structure */
508 if ((image = Read_PCX_to_Image(filename)) == NULL)
509 return PCX_FileInvalid;
512 printf("%s:\n", filename);
513 debug_print_timestamp(2, " READING PCX FILE TO IMAGE: ");
516 screen = DefaultScreen(display);
517 visual = DefaultVisual(display, screen);
518 depth = DefaultDepth(display, screen);
520 /* convert image structure to X11 Pixmap */
521 if (!(ximageinfo = Image_to_Pixmap(display, screen, visual,
522 window, gc, depth, image)))
523 Error(ERR_EXIT, "cannot convert Image to Pixmap");
525 /* if a private colormap has been created, install it */
526 if (ximageinfo->cmap != DefaultColormap(display, screen))
527 XSetWindowColormap(display, window, ximageinfo->cmap);
530 debug_print_timestamp(2, " CONVERTING IMAGE TO PIXMAP:");
533 /* create clip mask for the image */
534 ximageinfo->pixmap_mask = Image_to_Mask(image, display, window);
537 debug_print_timestamp(2, " CONVERTING IMAGE TO MASK: ");
540 *pixmap = ximageinfo->pixmap;
541 *pixmap_mask = ximageinfo->pixmap_mask;