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 ***********************************************************/
14 #if defined(TARGET_X11)
20 /* for MS-DOS/Allegro, exclude all except newImage() and freeImage() */
22 Image *newImage(unsigned int width, unsigned int height, unsigned int depth)
25 const unsigned int bytes_per_pixel = 1;
29 Error(ERR_EXIT, "images with more than 256 colors are not supported");
32 image = checked_malloc(sizeof(Image));
33 image->data = checked_malloc(width * height * bytes_per_pixel);
35 image->height = height;
38 for (i=0; i<MAX_COLORS; i++)
39 image->rgb.color_used[i] = FALSE;
44 void freeImage(Image *image)
50 #if defined(PLATFORM_UNIX)
52 /* extra colors to try allocating in private color maps to minimize flashing */
53 #define NOFLASH_COLORS 256
55 /* architecture independent value-to-memory conversion
56 note: the internal format is big endian */
58 #define value_to_memory(value, ptr, length) ( \
59 (length) == 1 ? (*( (byte *)(ptr) ) = ( value ) ) : \
60 (length) == 2 ? (*( (byte *)(ptr) ) = (((unsigned long)(value))>> 8), \
61 *(((byte *)(ptr))+1) = ( value ) ) : \
62 (length) == 3 ? (*( (byte *)(ptr) ) = (((unsigned long)(value))>>16), \
63 *(((byte *)(ptr))+1) = (((unsigned long)(value))>> 8), \
64 *(((byte *)(ptr))+2) = ( value ) ) : \
65 (*( (byte *)(ptr) ) = (((unsigned long)(value))>>24), \
66 *(((byte *)(ptr))+1) = (((unsigned long)(value))>>16), \
67 *(((byte *)(ptr))+2) = (((unsigned long)(value))>> 8), \
68 *(((byte *)(ptr))+3) = ( value ) ))
70 static Pixmap Image_to_Mask(Image *image, Display *display, Window window)
72 byte *src_ptr, *dst_ptr, *dst_ptr2;
73 unsigned int bytes_per_row;
79 bytes_per_row = (image->width + 7) / 8;
80 mask_data = checked_calloc(bytes_per_row * image->height);
82 src_ptr = image->data;
85 /* create bitmap data which can be used by 'XCreateBitmapFromData()'
86 * directly to create a pixmap of depth 1 for use as a clip mask for
87 * the corresponding image pixmap
90 for (y=0; y<image->height; y++)
92 bitmask = 0x01; /* start with leftmost bit in the byte */
93 dst_ptr2 = dst_ptr; /* start with leftmost byte in the row */
95 for (x=0; x<image->width; x++)
97 if (*src_ptr++) /* source pixel solid? (pixel index != 0) */
98 *dst_ptr2 |= bitmask; /* then write a bit into the image mask */
100 if ((bitmask <<= 1) == 0) /* bit at rightmost byte position reached? */
102 bitmask = 0x01; /* start again with leftmost bit position */
103 dst_ptr2++; /* continue with next byte in image mask */
107 dst_ptr += bytes_per_row; /* continue with leftmost byte of next row */
110 mask_pixmap = XCreateBitmapFromData(display, window, (char *)mask_data,
111 image->width, image->height);
117 static int bitsPerPixelAtDepth(Display *display, int screen, int depth)
119 XPixmapFormatValues *pixmap_format;
120 int i, num_pixmap_formats, bits_per_pixel = -1;
122 /* get Pixmap formats supported by the X server */
123 pixmap_format = XListPixmapFormats(display, &num_pixmap_formats);
125 /* find format that matches the given depth */
126 for (i=0; i<num_pixmap_formats; i++)
127 if (pixmap_format[i].depth == depth)
128 bits_per_pixel = pixmap_format[i].bits_per_pixel;
130 XFree(pixmap_format);
132 if (bits_per_pixel == -1)
133 Error(ERR_EXIT, "cannot find pixmap format for depth %d", depth);
135 return bits_per_pixel;
138 XImageInfo *Image_to_Pixmap(Display *display, int screen, Visual *visual,
139 Window window, GC gc, int depth, Image *image)
141 static XColor xcolor_private[NOFLASH_COLORS];
142 static int colorcell_used[NOFLASH_COLORS];
143 static Colormap global_cmap = 0;
144 static Pixel *global_cmap_index;
145 static int num_cmap_entries, free_cmap_entries;
146 static boolean private_cmap = FALSE;
147 Pixel *redvalue, *greenvalue, *bluevalue;
148 unsigned int a, c = 0, x, y, bytes_per_pixel, bits_per_pixel;
151 XImageInfo *ximageinfo;
152 byte *src_ptr, *dst_ptr;
156 if (visual == DefaultVisual(display, screen))
157 global_cmap = DefaultColormap(display, screen);
160 global_cmap = XCreateColormap(display, RootWindow(display, screen),
166 xcolor.flags = DoRed | DoGreen | DoBlue;
167 redvalue = greenvalue = bluevalue = NULL;
168 ximageinfo = checked_malloc(sizeof(XImageInfo));
169 ximageinfo->display = display;
170 ximageinfo->depth = depth;
172 switch (visual->class)
178 unsigned int redcolors, greencolors, bluecolors;
179 unsigned int redstep, greenstep, bluestep;
180 unsigned int redbottom, greenbottom, bluebottom;
181 unsigned int redtop, greentop, bluetop;
183 redvalue = (Pixel *)checked_malloc(sizeof(Pixel) * 256);
184 greenvalue = (Pixel *)checked_malloc(sizeof(Pixel) * 256);
185 bluevalue = (Pixel *)checked_malloc(sizeof(Pixel) * 256);
187 ximageinfo->cmap = global_cmap;
189 retry_direct: /* tag we hit if a DirectColor allocation fails on
190 * default colormap */
192 /* calculate number of distinct colors in each band */
194 redcolors = greencolors = bluecolors = 1;
195 for (pixval=1; pixval; pixval <<= 1)
197 if (pixval & visual->red_mask)
199 if (pixval & visual->green_mask)
201 if (pixval & visual->blue_mask)
205 /* consistency check */
206 if (redcolors > visual->map_entries ||
207 greencolors > visual->map_entries ||
208 bluecolors > visual->map_entries)
209 Error(ERR_WARN, "inconsistency in color information");
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++)
219 redtop = redbottom + redstep;
220 if (greenbottom < 256)
221 greentop = greenbottom + greenstep;
222 if (bluebottom < 256)
223 bluetop = bluebottom + bluestep;
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))
230 /* if an allocation fails for a DirectColor default visual then
231 we should create a private colormap and try again. */
233 if ((visual->class == DirectColor) &&
234 (visual == DefaultVisual(display, screen)))
236 global_cmap = XCopyColormapAndFree(display, global_cmap);
237 ximageinfo->cmap = global_cmap;
243 /* something completely unexpected happened */
245 fprintf(stderr, "imageToXImage: XAllocColor failed on a TrueColor/Directcolor visual\n");
253 /* fill in pixel values for each band at this intensity */
255 while ((redbottom < 256) && (redbottom < redtop))
256 redvalue[redbottom++] = xcolor.pixel & visual->red_mask;
257 while ((greenbottom < 256) && (greenbottom < greentop))
258 greenvalue[greenbottom++] = xcolor.pixel & visual->green_mask;
259 while ((bluebottom < 256) && (bluebottom < bluetop))
260 bluevalue[bluebottom++] = xcolor.pixel & visual->blue_mask;
267 ximageinfo->cmap = global_cmap;
269 for (a=0; a<MAX_COLORS; a++)
276 if (!image->rgb.color_used[a])
279 xcolor.red = *(image->rgb.red + a);
280 xcolor.green = *(image->rgb.green + a);
281 xcolor.blue = *(image->rgb.blue + a);
283 /* look if this color already exists in our colormap */
284 if (!XAllocColor(display, ximageinfo->cmap, &xcolor))
289 Error(ERR_RETURN, "switching to private colormap");
291 /* we just filled up the default colormap -- get a private one
292 which contains all already allocated colors */
294 global_cmap = XCopyColormapAndFree(display, global_cmap);
295 ximageinfo->cmap = global_cmap;
298 /* allocate the rest of the color cells read/write */
300 (Pixel *)checked_malloc(sizeof(Pixel) * NOFLASH_COLORS);
301 for (i=0; i<NOFLASH_COLORS; i++)
302 if (!XAllocColorCells(display, global_cmap, FALSE, NULL, 0,
303 global_cmap_index + i, 1))
305 num_cmap_entries = free_cmap_entries = i;
308 printf("We've got %d free colormap entries.\n", free_cmap_entries);
311 /* to minimize colormap flashing, copy default colors and try
312 to keep them as near as possible to the old values */
314 for(i=0; i<num_cmap_entries; i++)
316 xcolor2.pixel = *(global_cmap_index + i);
317 XQueryColor(display, DefaultColormap(display, screen), &xcolor2);
318 XStoreColor(display, global_cmap, &xcolor2);
319 xcolor_private[xcolor2.pixel] = xcolor2;
320 colorcell_used[xcolor2.pixel] = FALSE;
323 /* now we have the default colormap private: all colors we
324 successfully allocated so far are read-only, which is okay,
325 because we don't want to change them anymore -- if we need
326 an existing color again, we get it by XAllocColor; all other
327 colors are read/write and we can set them by XStoreColor,
328 but we will try to overwrite those color cells with our new
329 color which are as close as possible to our new color */
332 /* look for an existing default color close the one we want */
339 for (i=num_cmap_entries-1; i>=0; i--)
341 xcolor2.pixel = *(global_cmap_index + i);
342 xcolor2 = xcolor_private[xcolor2.pixel];
344 if (colorcell_used[xcolor2.pixel])
347 if ((xcolor.red & mask) == (xcolor2.red & mask) &&
348 (xcolor.green & mask) == (xcolor2.green & mask) &&
349 (xcolor.blue & mask) == (xcolor2.blue & mask))
352 printf("replacing color cell %ld with a close color\n",
363 mask = (mask << 1) & 0xffff;
366 if (!color_found) /* no more free color cells */
367 Error(ERR_EXIT, "cannot allocate enough color cells");
369 xcolor.pixel = xcolor2.pixel;
370 xcolor_private[xcolor.pixel] = xcolor;
371 colorcell_used[xcolor.pixel] = TRUE;
372 XStoreColor(display, ximageinfo->cmap, &xcolor);
376 *(ximageinfo->index + a) = xcolor.pixel;
380 printf("still %d free colormap entries\n", free_cmap_entries);
383 ximageinfo->no = a; /* number of pixels allocated for this image */
387 Error(ERR_RETURN, "display class not supported");
388 Error(ERR_EXIT, "DirectColor, TrueColor or PseudoColor display needed");
393 debug_print_timestamp(2, " ALLOCATING IMAGE COLORS: ");
396 /* create XImage from internal image structure and convert it to Pixmap */
398 bits_per_pixel = bitsPerPixelAtDepth(display, screen, depth);
399 bytes_per_pixel = (bits_per_pixel + 7) / 8;
401 ximage = XCreateImage(display, visual, depth, ZPixmap, 0,
402 NULL, image->width, image->height,
403 8, image->width * bytes_per_pixel);
405 checked_malloc(image->width * image->height * bytes_per_pixel);
406 ximage->byte_order = MSBFirst;
408 src_ptr = image->data;
409 dst_ptr = (byte *)ximage->data;
411 switch (visual->class)
418 for (y=0; y<image->height; y++) /* general case */
420 for (x=0; x<image->width; x++)
424 redvalue[image->rgb.red[pixval] >> 8] |
425 greenvalue[image->rgb.green[pixval] >> 8] |
426 bluevalue[image->rgb.blue[pixval] >> 8];
427 value_to_memory(pixval, dst_ptr, bytes_per_pixel);
428 dst_ptr += bytes_per_pixel;
436 if (bytes_per_pixel == 1) /* (common) special case */
438 for (y=0; y<image->height; y++)
439 for (x=0; x<image->width; x++)
440 *dst_ptr++ = ximageinfo->index[c + *src_ptr++];
442 else /* general case */
444 for (y=0; y<image->height; y++)
446 for (x=0; x<image->width; x++)
448 value_to_memory(ximageinfo->index[c + *src_ptr++],
449 dst_ptr, bytes_per_pixel);
450 dst_ptr += bytes_per_pixel;
458 Error(ERR_RETURN, "display class not supported");
459 Error(ERR_EXIT, "DirectColor, TrueColor or PseudoColor display needed");
465 free((byte *)redvalue);
466 free((byte *)greenvalue);
467 free((byte *)bluevalue);
471 debug_print_timestamp(2, " CONVERTING IMAGE TO XIMAGE:");
474 ximageinfo->pixmap = XCreatePixmap(display, window,
475 ximage->width, ximage->height,
478 XPutImage(ximageinfo->display, ximageinfo->pixmap, gc,
479 ximage, 0, 0, 0, 0, ximage->width, ximage->height);
483 XDestroyImage(ximage);
488 void freeXImage(Image *image, XImageInfo *ximageinfo)
490 if (ximageinfo->index != NULL && ximageinfo->no > 0)
491 XFreeColors(ximageinfo->display, ximageinfo->cmap, ximageinfo->index,
493 /* this ^^^^^^^^^^^^^^ is wrong, because the used color cells
494 * are somewhere between 0 and MAX_COLORS; there are indeed 'ximageinfo->no'
495 * used color cells, but they are not at array position 0 - 'ximageinfo->no'
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)
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;
553 #endif /* PLATFORM_UNIX */
554 #endif /* TARGET_X11 */