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 ***********************************************************/
16 #if defined(TARGET_X11)
22 /* for MS-DOS/Allegro, exclude all except newImage() and freeImage() */
24 Image *newImage(unsigned int width, unsigned int height, unsigned int depth)
27 const unsigned int bytes_per_pixel = 1;
31 Error(ERR_EXIT, "images with more than 256 colors are not supported");
34 image = checked_malloc(sizeof(Image));
35 image->data = checked_malloc(width * height * bytes_per_pixel);
37 image->height = height;
40 for (i=0; i<MAX_COLORS; i++)
41 image->rgb.color_used[i] = FALSE;
46 void freeImage(Image *image)
52 #if defined(PLATFORM_UNIX)
54 /* extra colors to try allocating in private color maps to minimize flashing */
55 #define NOFLASH_COLORS 256
57 /* architecture independent value-to-memory conversion
58 note: the internal format is big endian */
60 #define value_to_memory(value, ptr, length) ( \
61 (length) == 1 ? (*( (byte *)(ptr) ) = ( value ) ) : \
62 (length) == 2 ? (*( (byte *)(ptr) ) = (((unsigned long)(value))>> 8), \
63 *(((byte *)(ptr))+1) = ( value ) ) : \
64 (length) == 3 ? (*( (byte *)(ptr) ) = (((unsigned long)(value))>>16), \
65 *(((byte *)(ptr))+1) = (((unsigned long)(value))>> 8), \
66 *(((byte *)(ptr))+2) = ( value ) ) : \
67 (*( (byte *)(ptr) ) = (((unsigned long)(value))>>24), \
68 *(((byte *)(ptr))+1) = (((unsigned long)(value))>>16), \
69 *(((byte *)(ptr))+2) = (((unsigned long)(value))>> 8), \
70 *(((byte *)(ptr))+3) = ( value ) ))
72 static Pixmap Image_to_Mask(Image *image, Display *display, Window window)
74 byte *src_ptr, *dst_ptr, *dst_ptr2;
75 unsigned int bytes_per_row;
81 bytes_per_row = (image->width + 7) / 8;
82 mask_data = checked_calloc(bytes_per_row * image->height);
84 src_ptr = image->data;
87 /* create bitmap data which can be used by 'XCreateBitmapFromData()'
88 * directly to create a pixmap of depth 1 for use as a clip mask for
89 * the corresponding image pixmap
92 for (y=0; y<image->height; y++)
94 bitmask = 0x01; /* start with leftmost bit in the byte */
95 dst_ptr2 = dst_ptr; /* start with leftmost byte in the row */
97 for (x=0; x<image->width; x++)
99 if (*src_ptr++) /* source pixel solid? (pixel index != 0) */
100 *dst_ptr2 |= bitmask; /* then write a bit into the image mask */
102 if ((bitmask <<= 1) == 0) /* bit at rightmost byte position reached? */
104 bitmask = 0x01; /* start again with leftmost bit position */
105 dst_ptr2++; /* continue with next byte in image mask */
109 dst_ptr += bytes_per_row; /* continue with leftmost byte of next row */
112 mask_pixmap = XCreateBitmapFromData(display, window, (char *)mask_data,
113 image->width, image->height);
119 static int bitsPerPixelAtDepth(Display *display, int screen, int depth)
121 XPixmapFormatValues *pixmap_format;
122 int i, num_pixmap_formats, bits_per_pixel = -1;
124 /* get Pixmap formats supported by the X server */
125 pixmap_format = XListPixmapFormats(display, &num_pixmap_formats);
127 /* find format that matches the given depth */
128 for (i=0; i<num_pixmap_formats; i++)
129 if (pixmap_format[i].depth == depth)
130 bits_per_pixel = pixmap_format[i].bits_per_pixel;
132 XFree(pixmap_format);
134 if (bits_per_pixel == -1)
135 Error(ERR_EXIT, "cannot find pixmap format for depth %d", depth);
137 return bits_per_pixel;
140 XImageInfo *Image_to_Pixmap(Display *display, int screen, Visual *visual,
141 Window window, GC gc, int depth, Image *image)
143 static XColor xcolor_private[NOFLASH_COLORS];
144 static int colorcell_used[NOFLASH_COLORS];
145 static Colormap global_cmap = 0;
146 static Pixel *global_cmap_index;
147 static int num_cmap_entries, free_cmap_entries;
148 static boolean private_cmap = FALSE;
149 Pixel *redvalue, *greenvalue, *bluevalue;
150 unsigned int a, c = 0, x, y, bytes_per_pixel, bits_per_pixel;
153 XImageInfo *ximageinfo;
154 byte *src_ptr, *dst_ptr;
158 if (visual == DefaultVisual(display, screen))
159 global_cmap = DefaultColormap(display, screen);
162 global_cmap = XCreateColormap(display, RootWindow(display, screen),
168 xcolor.flags = DoRed | DoGreen | DoBlue;
169 redvalue = greenvalue = bluevalue = NULL;
170 ximageinfo = checked_malloc(sizeof(XImageInfo));
171 ximageinfo->display = display;
172 ximageinfo->depth = depth;
174 switch (visual->class)
180 unsigned int redcolors, greencolors, bluecolors;
181 unsigned int redstep, greenstep, bluestep;
182 unsigned int redbottom, greenbottom, bluebottom;
183 unsigned int redtop, greentop, bluetop;
185 redvalue = (Pixel *)checked_malloc(sizeof(Pixel) * 256);
186 greenvalue = (Pixel *)checked_malloc(sizeof(Pixel) * 256);
187 bluevalue = (Pixel *)checked_malloc(sizeof(Pixel) * 256);
189 ximageinfo->cmap = global_cmap;
191 retry_direct: /* tag we hit if a DirectColor allocation fails on
192 * default colormap */
194 /* calculate number of distinct colors in each band */
196 redcolors = greencolors = bluecolors = 1;
197 for (pixval=1; pixval; pixval <<= 1)
199 if (pixval & visual->red_mask)
201 if (pixval & visual->green_mask)
203 if (pixval & visual->blue_mask)
207 /* consistency check */
208 if (redcolors > visual->map_entries ||
209 greencolors > visual->map_entries ||
210 bluecolors > visual->map_entries)
211 Error(ERR_WARN, "inconsistency in color information");
213 redstep = 256 / redcolors;
214 greenstep = 256 / greencolors;
215 bluestep = 256 / bluecolors;
216 redbottom = greenbottom = bluebottom = 0;
217 redtop = greentop = bluetop = 0;
218 for (a=0; a<visual->map_entries; a++)
221 redtop = redbottom + redstep;
222 if (greenbottom < 256)
223 greentop = greenbottom + greenstep;
224 if (bluebottom < 256)
225 bluetop = bluebottom + bluestep;
227 xcolor.red = (redtop - 1) << 8;
228 xcolor.green = (greentop - 1) << 8;
229 xcolor.blue = (bluetop - 1) << 8;
230 if (!XAllocColor(display, ximageinfo->cmap, &xcolor))
232 /* if an allocation fails for a DirectColor default visual then
233 we should create a private colormap and try again. */
235 if ((visual->class == DirectColor) &&
236 (visual == DefaultVisual(display, screen)))
238 global_cmap = XCopyColormapAndFree(display, global_cmap);
239 ximageinfo->cmap = global_cmap;
245 /* something completely unexpected happened */
247 fprintf(stderr, "imageToXImage: XAllocColor failed on a TrueColor/Directcolor visual\n");
255 /* fill in pixel values for each band at this intensity */
257 while ((redbottom < 256) && (redbottom < redtop))
258 redvalue[redbottom++] = xcolor.pixel & visual->red_mask;
259 while ((greenbottom < 256) && (greenbottom < greentop))
260 greenvalue[greenbottom++] = xcolor.pixel & visual->green_mask;
261 while ((bluebottom < 256) && (bluebottom < bluetop))
262 bluevalue[bluebottom++] = xcolor.pixel & visual->blue_mask;
269 ximageinfo->cmap = global_cmap;
271 for (a=0; a<MAX_COLORS; a++)
278 if (!image->rgb.color_used[a])
281 xcolor.red = *(image->rgb.red + a);
282 xcolor.green = *(image->rgb.green + a);
283 xcolor.blue = *(image->rgb.blue + a);
285 /* look if this color already exists in our colormap */
286 if (!XAllocColor(display, ximageinfo->cmap, &xcolor))
291 Error(ERR_RETURN, "switching to private colormap");
293 /* we just filled up the default colormap -- get a private one
294 which contains all already allocated colors */
296 global_cmap = XCopyColormapAndFree(display, global_cmap);
297 ximageinfo->cmap = global_cmap;
300 /* allocate the rest of the color cells read/write */
302 (Pixel *)checked_malloc(sizeof(Pixel) * NOFLASH_COLORS);
303 for (i=0; i<NOFLASH_COLORS; i++)
304 if (!XAllocColorCells(display, global_cmap, FALSE, NULL, 0,
305 global_cmap_index + i, 1))
307 num_cmap_entries = free_cmap_entries = i;
310 printf("We've got %d free colormap entries.\n", free_cmap_entries);
313 /* to minimize colormap flashing, copy default colors and try
314 to keep them as near as possible to the old values */
316 for(i=0; i<num_cmap_entries; i++)
318 xcolor2.pixel = *(global_cmap_index + i);
319 XQueryColor(display, DefaultColormap(display, screen), &xcolor2);
320 XStoreColor(display, global_cmap, &xcolor2);
321 xcolor_private[xcolor2.pixel] = xcolor2;
322 colorcell_used[xcolor2.pixel] = FALSE;
325 /* now we have the default colormap private: all colors we
326 successfully allocated so far are read-only, which is okay,
327 because we don't want to change them anymore -- if we need
328 an existing color again, we get it by XAllocColor; all other
329 colors are read/write and we can set them by XStoreColor,
330 but we will try to overwrite those color cells with our new
331 color which are as close as possible to our new color */
334 /* look for an existing default color close the one we want */
341 for (i=num_cmap_entries-1; i>=0; i--)
343 xcolor2.pixel = *(global_cmap_index + i);
344 xcolor2 = xcolor_private[xcolor2.pixel];
346 if (colorcell_used[xcolor2.pixel])
349 if ((xcolor.red & mask) == (xcolor2.red & mask) &&
350 (xcolor.green & mask) == (xcolor2.green & mask) &&
351 (xcolor.blue & mask) == (xcolor2.blue & mask))
354 printf("replacing color cell %ld with a close color\n",
365 mask = (mask << 1) & 0xffff;
368 if (!color_found) /* no more free color cells */
369 Error(ERR_EXIT, "cannot allocate enough color cells");
371 xcolor.pixel = xcolor2.pixel;
372 xcolor_private[xcolor.pixel] = xcolor;
373 colorcell_used[xcolor.pixel] = TRUE;
374 XStoreColor(display, ximageinfo->cmap, &xcolor);
378 *(ximageinfo->index + a) = xcolor.pixel;
382 printf("still %d free colormap entries\n", free_cmap_entries);
385 ximageinfo->no = a; /* number of pixels allocated for this image */
389 Error(ERR_RETURN, "display class not supported");
390 Error(ERR_EXIT, "DirectColor, TrueColor or PseudoColor display needed");
395 debug_print_timestamp(2, " ALLOCATING IMAGE COLORS: ");
398 /* create XImage from internal image structure and convert it to Pixmap */
400 bits_per_pixel = bitsPerPixelAtDepth(display, screen, depth);
401 bytes_per_pixel = (bits_per_pixel + 7) / 8;
403 ximage = XCreateImage(display, visual, depth, ZPixmap, 0,
404 NULL, image->width, image->height,
405 8, image->width * bytes_per_pixel);
407 checked_malloc(image->width * image->height * bytes_per_pixel);
408 ximage->byte_order = MSBFirst;
410 src_ptr = image->data;
411 dst_ptr = (byte *)ximage->data;
413 switch (visual->class)
420 for (y=0; y<image->height; y++) /* general case */
422 for (x=0; x<image->width; x++)
426 redvalue[image->rgb.red[pixval] >> 8] |
427 greenvalue[image->rgb.green[pixval] >> 8] |
428 bluevalue[image->rgb.blue[pixval] >> 8];
429 value_to_memory(pixval, dst_ptr, bytes_per_pixel);
430 dst_ptr += bytes_per_pixel;
438 if (bytes_per_pixel == 1) /* (common) special case */
440 for (y=0; y<image->height; y++)
441 for (x=0; x<image->width; x++)
442 *dst_ptr++ = ximageinfo->index[c + *src_ptr++];
444 else /* general case */
446 for (y=0; y<image->height; y++)
448 for (x=0; x<image->width; x++)
450 value_to_memory(ximageinfo->index[c + *src_ptr++],
451 dst_ptr, bytes_per_pixel);
452 dst_ptr += bytes_per_pixel;
460 Error(ERR_RETURN, "display class not supported");
461 Error(ERR_EXIT, "DirectColor, TrueColor or PseudoColor display needed");
467 free((byte *)redvalue);
468 free((byte *)greenvalue);
469 free((byte *)bluevalue);
473 debug_print_timestamp(2, " CONVERTING IMAGE TO XIMAGE:");
476 ximageinfo->pixmap = XCreatePixmap(display, window,
477 ximage->width, ximage->height,
480 XPutImage(ximageinfo->display, ximageinfo->pixmap, gc,
481 ximage, 0, 0, 0, 0, ximage->width, ximage->height);
485 XDestroyImage(ximage);
490 void freeXImage(Image *image, XImageInfo *ximageinfo)
492 if (ximageinfo->index != NULL && ximageinfo->no > 0)
493 XFreeColors(ximageinfo->display, ximageinfo->cmap, ximageinfo->index,
495 /* this ^^^^^^^^^^^^^^ is wrong, because the used color cells
496 * are somewhere between 0 and MAX_COLORS; there are indeed 'ximageinfo->no'
497 * used color cells, but they are not at array position 0 - 'ximageinfo->no'
503 int Read_PCX_to_Pixmap(Display *display, Window window, GC gc, char *filename,
504 Pixmap *pixmap, Pixmap *pixmap_mask)
507 XImageInfo *ximageinfo;
513 debug_print_timestamp(2, NULL); /* initialize timestamp function */
516 /* read the graphic file in PCX format to image structure */
517 if ((image = Read_PCX_to_Image(filename)) == NULL)
521 printf("%s:\n", filename);
522 debug_print_timestamp(2, " READING PCX FILE TO IMAGE: ");
525 screen = DefaultScreen(display);
526 visual = DefaultVisual(display, screen);
527 depth = DefaultDepth(display, screen);
529 /* convert image structure to X11 Pixmap */
530 if (!(ximageinfo = Image_to_Pixmap(display, screen, visual,
531 window, gc, depth, image)))
532 Error(ERR_EXIT, "cannot convert Image to Pixmap");
534 /* if a private colormap has been created, install it */
535 if (ximageinfo->cmap != DefaultColormap(display, screen))
536 XSetWindowColormap(display, window, ximageinfo->cmap);
539 debug_print_timestamp(2, " CONVERTING IMAGE TO PIXMAP:");
542 /* create clip mask for the image */
543 ximageinfo->pixmap_mask = Image_to_Mask(image, display, window);
546 debug_print_timestamp(2, " CONVERTING IMAGE TO MASK: ");
549 *pixmap = ximageinfo->pixmap;
550 *pixmap_mask = ximageinfo->pixmap_mask;
555 #endif /* PLATFORM_UNIX */
556 #endif /* TARGET_X11 */