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 #ifndef USE_SDL_LIBRARY
20 /* exclude all except newImage() and freeImage() */
23 /* extra colors to try allocating in private color maps to minimize flashing */
24 #define NOFLASH_COLORS 256
26 /* architecture independent value-to-memory conversion
27 note: the internal format is big endian */
29 #define value_to_memory(value, ptr, length) ( \
30 (length) == 1 ? (*( (byte *)(ptr) ) = ( value ) ) : \
31 (length) == 2 ? (*( (byte *)(ptr) ) = (((unsigned long)(value))>> 8), \
32 *(((byte *)(ptr))+1) = ( value ) ) : \
33 (length) == 3 ? (*( (byte *)(ptr) ) = (((unsigned long)(value))>>16), \
34 *(((byte *)(ptr))+1) = (((unsigned long)(value))>> 8), \
35 *(((byte *)(ptr))+2) = ( value ) ) : \
36 (*( (byte *)(ptr) ) = (((unsigned long)(value))>>24), \
37 *(((byte *)(ptr))+1) = (((unsigned long)(value))>>16), \
38 *(((byte *)(ptr))+2) = (((unsigned long)(value))>> 8), \
39 *(((byte *)(ptr))+3) = ( value ) ))
41 static Pixmap Image_to_Mask(Image *image, Display *display, Window window)
43 byte *src_ptr, *dst_ptr, *dst_ptr2;
44 unsigned int bytes_per_row;
50 bytes_per_row = (image->width + 7) / 8;
51 mask_data = checked_calloc(bytes_per_row * image->height);
53 src_ptr = image->data;
56 /* create bitmap data which can be used by 'XCreateBitmapFromData()'
57 * directly to create a pixmap of depth 1 for use as a clip mask for
58 * the corresponding image pixmap
61 for (y=0; y<image->height; y++)
63 bitmask = 0x01; /* start with leftmost bit in the byte */
64 dst_ptr2 = dst_ptr; /* start with leftmost byte in the row */
66 for (x=0; x<image->width; x++)
68 if (*src_ptr++) /* source pixel solid? (pixel index != 0) */
69 *dst_ptr2 |= bitmask; /* then write a bit into the image mask */
71 if ((bitmask <<= 1) == 0) /* bit at rightmost byte position reached? */
73 bitmask = 0x01; /* start again with leftmost bit position */
74 dst_ptr2++; /* continue with next byte in image mask */
78 dst_ptr += bytes_per_row; /* continue with leftmost byte of next row */
81 mask_pixmap = XCreateBitmapFromData(display, window, (char *)mask_data,
82 image->width, image->height);
88 static int bitsPerPixelAtDepth(Display *display, int screen, int depth)
90 XPixmapFormatValues *pixmap_format;
91 int i, num_pixmap_formats, bits_per_pixel = -1;
93 /* get Pixmap formats supported by the X server */
94 pixmap_format = XListPixmapFormats(display, &num_pixmap_formats);
96 /* find format that matches the given depth */
97 for (i=0; i<num_pixmap_formats; i++)
98 if (pixmap_format[i].depth == depth)
99 bits_per_pixel = pixmap_format[i].bits_per_pixel;
101 XFree(pixmap_format);
103 if (bits_per_pixel == -1)
104 Error(ERR_EXIT, "cannot find pixmap format for depth %d", depth);
106 return bits_per_pixel;
109 XImageInfo *Image_to_Pixmap(Display *display, int screen, Visual *visual,
110 Window window, GC gc, int depth, Image *image)
112 static XColor xcolor_private[NOFLASH_COLORS];
113 static int colorcell_used[NOFLASH_COLORS];
114 static Colormap global_cmap = 0;
115 static Pixel *global_cmap_index;
116 static int num_cmap_entries, free_cmap_entries;
117 static boolean private_cmap = FALSE;
118 Pixel *redvalue, *greenvalue, *bluevalue;
119 unsigned int a, c = 0, x, y, bytes_per_pixel, bits_per_pixel;
122 XImageInfo *ximageinfo;
123 byte *src_ptr, *dst_ptr;
127 if (visual == DefaultVisual(display, screen))
128 global_cmap = DefaultColormap(display, screen);
131 global_cmap = XCreateColormap(display, RootWindow(display, screen),
137 xcolor.flags = DoRed | DoGreen | DoBlue;
138 redvalue = greenvalue = bluevalue = NULL;
139 ximageinfo = checked_malloc(sizeof(XImageInfo));
140 ximageinfo->display = display;
141 ximageinfo->depth = depth;
143 switch (visual->class)
149 unsigned int redcolors, greencolors, bluecolors;
150 unsigned int redstep, greenstep, bluestep;
151 unsigned int redbottom, greenbottom, bluebottom;
152 unsigned int redtop, greentop, bluetop;
154 redvalue = (Pixel *)checked_malloc(sizeof(Pixel) * 256);
155 greenvalue = (Pixel *)checked_malloc(sizeof(Pixel) * 256);
156 bluevalue = (Pixel *)checked_malloc(sizeof(Pixel) * 256);
158 ximageinfo->cmap = global_cmap;
160 retry_direct: /* tag we hit if a DirectColor allocation fails on
161 * default colormap */
163 /* calculate number of distinct colors in each band */
165 redcolors = greencolors = bluecolors = 1;
166 for (pixval=1; pixval; pixval <<= 1)
168 if (pixval & visual->red_mask)
170 if (pixval & visual->green_mask)
172 if (pixval & visual->blue_mask)
176 /* consistency check */
177 if (redcolors > visual->map_entries ||
178 greencolors > visual->map_entries ||
179 bluecolors > visual->map_entries)
180 Error(ERR_WARN, "inconsistency in color information");
182 redstep = 256 / redcolors;
183 greenstep = 256 / greencolors;
184 bluestep = 256 / bluecolors;
185 redbottom = greenbottom = bluebottom = 0;
186 redtop = greentop = bluetop = 0;
187 for (a=0; a<visual->map_entries; a++)
190 redtop = redbottom + redstep;
191 if (greenbottom < 256)
192 greentop = greenbottom + greenstep;
193 if (bluebottom < 256)
194 bluetop = bluebottom + bluestep;
196 xcolor.red = (redtop - 1) << 8;
197 xcolor.green = (greentop - 1) << 8;
198 xcolor.blue = (bluetop - 1) << 8;
199 if (!XAllocColor(display, ximageinfo->cmap, &xcolor))
201 /* if an allocation fails for a DirectColor default visual then
202 we should create a private colormap and try again. */
204 if ((visual->class == DirectColor) &&
205 (visual == DefaultVisual(display, screen)))
207 global_cmap = XCopyColormapAndFree(display, global_cmap);
208 ximageinfo->cmap = global_cmap;
214 /* something completely unexpected happened */
216 fprintf(stderr, "imageToXImage: XAllocColor failed on a TrueColor/Directcolor visual\n");
224 /* fill in pixel values for each band at this intensity */
226 while ((redbottom < 256) && (redbottom < redtop))
227 redvalue[redbottom++] = xcolor.pixel & visual->red_mask;
228 while ((greenbottom < 256) && (greenbottom < greentop))
229 greenvalue[greenbottom++] = xcolor.pixel & visual->green_mask;
230 while ((bluebottom < 256) && (bluebottom < bluetop))
231 bluevalue[bluebottom++] = xcolor.pixel & visual->blue_mask;
238 ximageinfo->cmap = global_cmap;
240 for (a=0; a<MAX_COLORS; a++)
247 if (!image->rgb.color_used[a])
250 xcolor.red = *(image->rgb.red + a);
251 xcolor.green = *(image->rgb.green + a);
252 xcolor.blue = *(image->rgb.blue + a);
254 /* look if this color already exists in our colormap */
255 if (!XAllocColor(display, ximageinfo->cmap, &xcolor))
260 Error(ERR_RETURN, "switching to private colormap");
262 /* we just filled up the default colormap -- get a private one
263 which contains all already allocated colors */
265 global_cmap = XCopyColormapAndFree(display, global_cmap);
266 ximageinfo->cmap = global_cmap;
269 /* allocate the rest of the color cells read/write */
271 (Pixel *)checked_malloc(sizeof(Pixel) * NOFLASH_COLORS);
272 for (i=0; i<NOFLASH_COLORS; i++)
273 if (!XAllocColorCells(display, global_cmap, FALSE, NULL, 0,
274 global_cmap_index + i, 1))
276 num_cmap_entries = free_cmap_entries = i;
279 printf("We've got %d free colormap entries.\n", free_cmap_entries);
282 /* to minimize colormap flashing, copy default colors and try
283 to keep them as near as possible to the old values */
285 for(i=0; i<num_cmap_entries; i++)
287 xcolor2.pixel = *(global_cmap_index + i);
288 XQueryColor(display, DefaultColormap(display, screen), &xcolor2);
289 XStoreColor(display, global_cmap, &xcolor2);
290 xcolor_private[xcolor2.pixel] = xcolor2;
291 colorcell_used[xcolor2.pixel] = FALSE;
294 /* now we have the default colormap private: all colors we
295 successfully allocated so far are read-only, which is okay,
296 because we don't want to change them anymore -- if we need
297 an existing color again, we get it by XAllocColor; all other
298 colors are read/write and we can set them by XStoreColor,
299 but we will try to overwrite those color cells with our new
300 color which are as close as possible to our new color */
303 /* look for an existing default color close the one we want */
310 for (i=num_cmap_entries-1; i>=0; i--)
312 xcolor2.pixel = *(global_cmap_index + i);
313 xcolor2 = xcolor_private[xcolor2.pixel];
315 if (colorcell_used[xcolor2.pixel])
318 if ((xcolor.red & mask) == (xcolor2.red & mask) &&
319 (xcolor.green & mask) == (xcolor2.green & mask) &&
320 (xcolor.blue & mask) == (xcolor2.blue & mask))
323 printf("replacing color cell %ld with a close color\n",
334 mask = (mask << 1) & 0xffff;
337 if (!color_found) /* no more free color cells */
338 Error(ERR_EXIT, "cannot allocate enough color cells");
340 xcolor.pixel = xcolor2.pixel;
341 xcolor_private[xcolor.pixel] = xcolor;
342 colorcell_used[xcolor.pixel] = TRUE;
343 XStoreColor(display, ximageinfo->cmap, &xcolor);
347 *(ximageinfo->index + a) = xcolor.pixel;
351 printf("still %d free colormap entries\n", free_cmap_entries);
354 ximageinfo->no = a; /* number of pixels allocated for this image */
358 Error(ERR_RETURN, "display class not supported");
359 Error(ERR_EXIT, "DirectColor, TrueColor or PseudoColor display needed");
364 debug_print_timestamp(2, " ALLOCATING IMAGE COLORS: ");
367 /* create XImage from internal image structure and convert it to Pixmap */
369 bits_per_pixel = bitsPerPixelAtDepth(display, screen, depth);
370 bytes_per_pixel = (bits_per_pixel + 7) / 8;
372 ximage = XCreateImage(display, visual, depth, ZPixmap, 0,
373 NULL, image->width, image->height,
374 8, image->width * bytes_per_pixel);
376 checked_malloc(image->width * image->height * bytes_per_pixel);
377 ximage->byte_order = MSBFirst;
379 src_ptr = image->data;
380 dst_ptr = (byte *)ximage->data;
382 switch (visual->class)
389 for (y=0; y<image->height; y++) /* general case */
391 for (x=0; x<image->width; x++)
395 redvalue[image->rgb.red[pixval] >> 8] |
396 greenvalue[image->rgb.green[pixval] >> 8] |
397 bluevalue[image->rgb.blue[pixval] >> 8];
398 value_to_memory(pixval, dst_ptr, bytes_per_pixel);
399 dst_ptr += bytes_per_pixel;
407 if (bytes_per_pixel == 1) /* (common) special case */
409 for (y=0; y<image->height; y++)
410 for (x=0; x<image->width; x++)
411 *dst_ptr++ = ximageinfo->index[c + *src_ptr++];
413 else /* general case */
415 for (y=0; y<image->height; y++)
417 for (x=0; x<image->width; x++)
419 value_to_memory(ximageinfo->index[c + *src_ptr++],
420 dst_ptr, bytes_per_pixel);
421 dst_ptr += bytes_per_pixel;
429 Error(ERR_RETURN, "display class not supported");
430 Error(ERR_EXIT, "DirectColor, TrueColor or PseudoColor display needed");
436 free((byte *)redvalue);
437 free((byte *)greenvalue);
438 free((byte *)bluevalue);
442 debug_print_timestamp(2, " CONVERTING IMAGE TO XIMAGE:");
445 ximageinfo->pixmap = XCreatePixmap(display, window,
446 ximage->width, ximage->height,
449 XPutImage(ximageinfo->display, ximageinfo->pixmap, gc,
450 ximage, 0, 0, 0, 0, ximage->width, ximage->height);
454 XDestroyImage(ximage);
459 void freeXImage(Image *image, XImageInfo *ximageinfo)
461 if (ximageinfo->index != NULL && ximageinfo->no > 0)
462 XFreeColors(ximageinfo->display, ximageinfo->cmap, ximageinfo->index,
464 /* this ^^^^^^^^^^^^^^ is wrong, because the used color cells
465 * are somewhere between 0 and MAX_COLORS; there are indeed 'ximageinfo->no'
466 * used color cells, but they are not at array position 0 - 'ximageinfo->no'
474 Image *newImage(unsigned int width, unsigned int height, unsigned int depth)
477 const unsigned int bytes_per_pixel = 1;
481 Error(ERR_EXIT, "images with more than 256 colors are not supported");
484 image = checked_malloc(sizeof(Image));
485 image->data = checked_malloc(width * height * bytes_per_pixel);
486 image->width = width;
487 image->height = height;
488 image->depth = depth;
490 for (i=0; i<MAX_COLORS; i++)
491 image->rgb.color_used[i] = FALSE;
496 void freeImage(Image *image)
504 int Read_PCX_to_Pixmap(Display *display, Window window, GC gc, char *filename,
505 Pixmap *pixmap, Pixmap *pixmap_mask)
508 XImageInfo *ximageinfo;
514 debug_print_timestamp(2, NULL); /* initialize timestamp function */
517 /* read the graphic file in PCX format to image structure */
518 if ((image = Read_PCX_to_Image(filename)) == NULL)
522 printf("%s:\n", filename);
523 debug_print_timestamp(2, " READING PCX FILE TO IMAGE: ");
526 screen = DefaultScreen(display);
527 visual = DefaultVisual(display, screen);
528 depth = DefaultDepth(display, screen);
530 /* convert image structure to X11 Pixmap */
531 if (!(ximageinfo = Image_to_Pixmap(display, screen, visual,
532 window, gc, depth, image)))
533 Error(ERR_EXIT, "cannot convert Image to Pixmap");
535 /* if a private colormap has been created, install it */
536 if (ximageinfo->cmap != DefaultColormap(display, screen))
537 XSetWindowColormap(display, window, ximageinfo->cmap);
540 debug_print_timestamp(2, " CONVERTING IMAGE TO PIXMAP:");
543 /* create clip mask for the image */
544 ximageinfo->pixmap_mask = Image_to_Mask(image, display, window);
547 debug_print_timestamp(2, " CONVERTING IMAGE TO MASK: ");
550 *pixmap = ximageinfo->pixmap;
551 *pixmap_mask = ximageinfo->pixmap_mask;
556 #endif /* !USE_SDL_LIBRARY */