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 ***********************************************************/
18 /* exclude all except newImage() and freeImage() */
21 /* extra colors to try allocating in private color maps to minimize flashing */
22 #define NOFLASH_COLORS 256
24 /* architecture independent value-to-memory conversion
25 note: the internal format is big endian */
27 #define value_to_memory(value, ptr, length) ( \
28 (length) == 1 ? (*( (byte *)(ptr) ) = ( value ) ) : \
29 (length) == 2 ? (*( (byte *)(ptr) ) = (((unsigned long)(value))>> 8), \
30 *(((byte *)(ptr))+1) = ( value ) ) : \
31 (length) == 3 ? (*( (byte *)(ptr) ) = (((unsigned long)(value))>>16), \
32 *(((byte *)(ptr))+1) = (((unsigned long)(value))>> 8), \
33 *(((byte *)(ptr))+2) = ( value ) ) : \
34 (*( (byte *)(ptr) ) = (((unsigned long)(value))>>24), \
35 *(((byte *)(ptr))+1) = (((unsigned long)(value))>>16), \
36 *(((byte *)(ptr))+2) = (((unsigned long)(value))>> 8), \
37 *(((byte *)(ptr))+3) = ( value ) ))
39 static Pixmap Image_to_Mask(Image *image, Display *display, Window window)
41 byte *src_ptr, *dst_ptr, *dst_ptr2;
42 unsigned int bytes_per_row;
48 bytes_per_row = (image->width + 7) / 8;
49 mask_data = checked_calloc(bytes_per_row * image->height);
51 src_ptr = image->data;
54 /* create bitmap data which can be used by 'XCreateBitmapFromData()'
55 * directly to create a pixmap of depth 1 for use as a clip mask for
56 * the corresponding image pixmap
59 for (y=0; y<image->height; y++)
61 bitmask = 0x01; /* start with leftmost bit in the byte */
62 dst_ptr2 = dst_ptr; /* start with leftmost byte in the row */
64 for (x=0; x<image->width; x++)
66 if (*src_ptr++) /* source pixel solid? (pixel index != 0) */
67 *dst_ptr2 |= bitmask; /* then write a bit into the image mask */
69 if ((bitmask <<= 1) == 0) /* bit at rightmost byte position reached? */
71 bitmask = 0x01; /* start again with leftmost bit position */
72 dst_ptr2++; /* continue with next byte in image mask */
76 dst_ptr += bytes_per_row; /* continue with leftmost byte of next row */
79 mask_pixmap = XCreateBitmapFromData(display, window, (char *)mask_data,
80 image->width, image->height);
86 static int bitsPerPixelAtDepth(Display *display, int screen, int depth)
88 XPixmapFormatValues *pixmap_format;
89 int i, num_pixmap_formats, bits_per_pixel = -1;
91 /* get Pixmap formats supported by the X server */
92 pixmap_format = XListPixmapFormats(display, &num_pixmap_formats);
94 /* find format that matches the given depth */
95 for (i=0; i<num_pixmap_formats; i++)
96 if (pixmap_format[i].depth == depth)
97 bits_per_pixel = pixmap_format[i].bits_per_pixel;
101 if (bits_per_pixel == -1)
102 Error(ERR_EXIT, "cannot find pixmap format for depth %d", depth);
104 return bits_per_pixel;
107 XImageInfo *Image_to_Pixmap(Display *display, int screen, Visual *visual,
108 Window window, GC gc, int depth, Image *image)
110 static XColor xcolor_private[NOFLASH_COLORS];
111 static int colorcell_used[NOFLASH_COLORS];
112 static Colormap global_cmap = 0;
113 static Pixel *global_cmap_index;
114 static int num_cmap_entries, free_cmap_entries;
115 static boolean private_cmap = FALSE;
116 Pixel *redvalue, *greenvalue, *bluevalue;
117 unsigned int a, c = 0, x, y, bytes_per_pixel, bits_per_pixel;
120 XImageInfo *ximageinfo;
121 byte *src_ptr, *dst_ptr;
125 if (visual == DefaultVisual(display, screen))
126 global_cmap = DefaultColormap(display, screen);
129 global_cmap = XCreateColormap(display, RootWindow(display, screen),
135 xcolor.flags = DoRed | DoGreen | DoBlue;
136 redvalue = greenvalue = bluevalue = NULL;
137 ximageinfo = checked_malloc(sizeof(XImageInfo));
138 ximageinfo->display = display;
139 ximageinfo->depth = depth;
141 switch (visual->class)
147 unsigned int redcolors, greencolors, bluecolors;
148 unsigned int redstep, greenstep, bluestep;
149 unsigned int redbottom, greenbottom, bluebottom;
150 unsigned int redtop, greentop, bluetop;
152 redvalue = (Pixel *)checked_malloc(sizeof(Pixel) * 256);
153 greenvalue = (Pixel *)checked_malloc(sizeof(Pixel) * 256);
154 bluevalue = (Pixel *)checked_malloc(sizeof(Pixel) * 256);
156 ximageinfo->cmap = global_cmap;
158 retry_direct: /* tag we hit if a DirectColor allocation fails on
159 * default colormap */
161 /* calculate number of distinct colors in each band */
163 redcolors = greencolors = bluecolors = 1;
164 for (pixval=1; pixval; pixval <<= 1)
166 if (pixval & visual->red_mask)
168 if (pixval & visual->green_mask)
170 if (pixval & visual->blue_mask)
174 /* consistency check */
175 if (redcolors > visual->map_entries ||
176 greencolors > visual->map_entries ||
177 bluecolors > visual->map_entries)
178 Error(ERR_WARN, "inconsistency in color information");
180 redstep = 256 / redcolors;
181 greenstep = 256 / greencolors;
182 bluestep = 256 / bluecolors;
183 redbottom = greenbottom = bluebottom = 0;
184 redtop = greentop = bluetop = 0;
185 for (a=0; a<visual->map_entries; a++)
188 redtop = redbottom + redstep;
189 if (greenbottom < 256)
190 greentop = greenbottom + greenstep;
191 if (bluebottom < 256)
192 bluetop = bluebottom + bluestep;
194 xcolor.red = (redtop - 1) << 8;
195 xcolor.green = (greentop - 1) << 8;
196 xcolor.blue = (bluetop - 1) << 8;
197 if (!XAllocColor(display, ximageinfo->cmap, &xcolor))
199 /* if an allocation fails for a DirectColor default visual then
200 we should create a private colormap and try again. */
202 if ((visual->class == DirectColor) &&
203 (visual == DefaultVisual(display, screen)))
205 global_cmap = XCopyColormapAndFree(display, global_cmap);
206 ximageinfo->cmap = global_cmap;
212 /* something completely unexpected happened */
214 fprintf(stderr, "imageToXImage: XAllocColor failed on a TrueColor/Directcolor visual\n");
222 /* fill in pixel values for each band at this intensity */
224 while ((redbottom < 256) && (redbottom < redtop))
225 redvalue[redbottom++] = xcolor.pixel & visual->red_mask;
226 while ((greenbottom < 256) && (greenbottom < greentop))
227 greenvalue[greenbottom++] = xcolor.pixel & visual->green_mask;
228 while ((bluebottom < 256) && (bluebottom < bluetop))
229 bluevalue[bluebottom++] = xcolor.pixel & visual->blue_mask;
236 ximageinfo->cmap = global_cmap;
238 for (a=0; a<MAX_COLORS; a++)
245 if (!image->rgb.color_used[a])
248 xcolor.red = *(image->rgb.red + a);
249 xcolor.green = *(image->rgb.green + a);
250 xcolor.blue = *(image->rgb.blue + a);
252 /* look if this color already exists in our colormap */
253 if (!XAllocColor(display, ximageinfo->cmap, &xcolor))
258 Error(ERR_RETURN, "switching to private colormap");
260 /* we just filled up the default colormap -- get a private one
261 which contains all already allocated colors */
263 global_cmap = XCopyColormapAndFree(display, global_cmap);
264 ximageinfo->cmap = global_cmap;
267 /* allocate the rest of the color cells read/write */
269 (Pixel *)checked_malloc(sizeof(Pixel) * NOFLASH_COLORS);
270 for (i=0; i<NOFLASH_COLORS; i++)
271 if (!XAllocColorCells(display, global_cmap, FALSE, NULL, 0,
272 global_cmap_index + i, 1))
274 num_cmap_entries = free_cmap_entries = i;
277 printf("We've got %d free colormap entries.\n", free_cmap_entries);
280 /* to minimize colormap flashing, copy default colors and try
281 to keep them as near as possible to the old values */
283 for(i=0; i<num_cmap_entries; i++)
285 xcolor2.pixel = *(global_cmap_index + i);
286 XQueryColor(display, DefaultColormap(display, screen), &xcolor2);
287 XStoreColor(display, global_cmap, &xcolor2);
288 xcolor_private[xcolor2.pixel] = xcolor2;
289 colorcell_used[xcolor2.pixel] = FALSE;
292 /* now we have the default colormap private: all colors we
293 successfully allocated so far are read-only, which is okay,
294 because we don't want to change them anymore -- if we need
295 an existing color again, we get it by XAllocColor; all other
296 colors are read/write and we can set them by XStoreColor,
297 but we will try to overwrite those color cells with our new
298 color which are as close as possible to our new color */
301 /* look for an existing default color close the one we want */
308 for (i=num_cmap_entries-1; i>=0; i--)
310 xcolor2.pixel = *(global_cmap_index + i);
311 xcolor2 = xcolor_private[xcolor2.pixel];
313 if (colorcell_used[xcolor2.pixel])
316 if ((xcolor.red & mask) == (xcolor2.red & mask) &&
317 (xcolor.green & mask) == (xcolor2.green & mask) &&
318 (xcolor.blue & mask) == (xcolor2.blue & mask))
321 printf("replacing color cell %ld with a close color\n",
332 mask = (mask << 1) & 0xffff;
335 if (!color_found) /* no more free color cells */
336 Error(ERR_EXIT, "cannot allocate enough color cells");
338 xcolor.pixel = xcolor2.pixel;
339 xcolor_private[xcolor.pixel] = xcolor;
340 colorcell_used[xcolor.pixel] = TRUE;
341 XStoreColor(display, ximageinfo->cmap, &xcolor);
345 *(ximageinfo->index + a) = xcolor.pixel;
349 printf("still %d free colormap entries\n", free_cmap_entries);
352 ximageinfo->no = a; /* number of pixels allocated for this image */
356 Error(ERR_RETURN, "display class not supported");
357 Error(ERR_EXIT, "DirectColor, TrueColor or PseudoColor display needed");
362 debug_print_timestamp(2, " ALLOCATING IMAGE COLORS: ");
365 /* create XImage from internal image structure and convert it to Pixmap */
367 bits_per_pixel = bitsPerPixelAtDepth(display, screen, depth);
368 bytes_per_pixel = (bits_per_pixel + 7) / 8;
370 ximage = XCreateImage(display, visual, depth, ZPixmap, 0,
371 NULL, image->width, image->height,
372 8, image->width * bytes_per_pixel);
374 checked_malloc(image->width * image->height * bytes_per_pixel);
375 ximage->byte_order = MSBFirst;
377 src_ptr = image->data;
378 dst_ptr = (byte *)ximage->data;
380 switch (visual->class)
387 for (y=0; y<image->height; y++) /* general case */
389 for (x=0; x<image->width; x++)
393 redvalue[image->rgb.red[pixval] >> 8] |
394 greenvalue[image->rgb.green[pixval] >> 8] |
395 bluevalue[image->rgb.blue[pixval] >> 8];
396 value_to_memory(pixval, dst_ptr, bytes_per_pixel);
397 dst_ptr += bytes_per_pixel;
405 if (bytes_per_pixel == 1) /* (common) special case */
407 for (y=0; y<image->height; y++)
408 for (x=0; x<image->width; x++)
409 *dst_ptr++ = ximageinfo->index[c + *src_ptr++];
411 else /* general case */
413 for (y=0; y<image->height; y++)
415 for (x=0; x<image->width; x++)
417 value_to_memory(ximageinfo->index[c + *src_ptr++],
418 dst_ptr, bytes_per_pixel);
419 dst_ptr += bytes_per_pixel;
427 Error(ERR_RETURN, "display class not supported");
428 Error(ERR_EXIT, "DirectColor, TrueColor or PseudoColor display needed");
434 free((byte *)redvalue);
435 free((byte *)greenvalue);
436 free((byte *)bluevalue);
440 debug_print_timestamp(2, " CONVERTING IMAGE TO XIMAGE:");
443 ximageinfo->pixmap = XCreatePixmap(display, window,
444 ximage->width, ximage->height,
447 XPutImage(ximageinfo->display, ximageinfo->pixmap, gc,
448 ximage, 0, 0, 0, 0, ximage->width, ximage->height);
452 XDestroyImage(ximage);
457 void freeXImage(Image *image, XImageInfo *ximageinfo)
459 if (ximageinfo->index != NULL && ximageinfo->no > 0)
460 XFreeColors(ximageinfo->display, ximageinfo->cmap, ximageinfo->index,
462 /* this ^^^^^^^^^^^^^^ is wrong, because the used color cells
463 * are somewhere between 0 and MAX_COLORS; there are indeed 'ximageinfo->no'
464 * used color cells, but they are not at array position 0 - 'ximageinfo->no'
472 Image *newImage(unsigned int width, unsigned int height, unsigned int depth)
475 const unsigned int bytes_per_pixel = 1;
479 Error(ERR_EXIT, "images with more than 256 colors are not supported");
482 image = checked_malloc(sizeof(Image));
483 image->data = checked_malloc(width * height * bytes_per_pixel);
484 image->width = width;
485 image->height = height;
486 image->depth = depth;
488 for (i=0; i<MAX_COLORS; i++)
489 image->rgb.color_used[i] = FALSE;
494 void freeImage(Image *image)
502 int Read_PCX_to_Pixmap(Display *display, Window window, GC gc, char *filename,
503 Pixmap *pixmap, Pixmap *pixmap_mask)
506 XImageInfo *ximageinfo;
512 debug_print_timestamp(2, NULL); /* initialize timestamp function */
515 /* read the graphic file in PCX format to image structure */
516 if ((image = Read_PCX_to_Image(filename)) == NULL)
520 printf("%s:\n", filename);
521 debug_print_timestamp(2, " READING PCX FILE TO IMAGE: ");
524 screen = DefaultScreen(display);
525 visual = DefaultVisual(display, screen);
526 depth = DefaultDepth(display, screen);
528 /* convert image structure to X11 Pixmap */
529 if (!(ximageinfo = Image_to_Pixmap(display, screen, visual,
530 window, gc, depth, image)))
531 Error(ERR_EXIT, "cannot convert Image to Pixmap");
533 /* if a private colormap has been created, install it */
534 if (ximageinfo->cmap != DefaultColormap(display, screen))
535 XSetWindowColormap(display, window, ximageinfo->cmap);
538 debug_print_timestamp(2, " CONVERTING IMAGE TO PIXMAP:");
541 /* create clip mask for the image */
542 ximageinfo->pixmap_mask = Image_to_Mask(image, display, window);
545 debug_print_timestamp(2, " CONVERTING IMAGE TO MASK: ");
548 *pixmap = ximageinfo->pixmap;
549 *pixmap_mask = ximageinfo->pixmap_mask;