1 /***********************************************************
2 * Artsoft Retro-Game Library *
3 *----------------------------------------------------------*
4 * (c) 1994-2001 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
19 #if defined(TARGET_X11)
21 /* for MS-DOS/Allegro, exclude all except newImage() and freeImage() */
23 Image *newImage(unsigned int width, unsigned int height, unsigned int depth)
26 unsigned int bytes_per_pixel = (depth + 7) / 8;
31 Error(ERR_EXIT, "images with more than 256 colors are not supported");
36 image = checked_malloc(sizeof(Image));
37 image->data = checked_malloc(width * height * bytes_per_pixel);
39 image->height = height;
41 image->bytes_per_pixel = bytes_per_pixel;
42 image->bytes_per_row = width * bytes_per_pixel;
45 for (i=0; i<MAX_COLORS; i++)
46 image->rgb.color_used[i] = FALSE;
48 image->type = (depth < 8 ? IMAGETYPE_BITMAP :
49 depth > 8 ? IMAGETYPE_TRUECOLOR : IMAGETYPE_RGB);
54 void freeImage(Image *image)
60 #if defined(PLATFORM_UNIX)
62 /* extra colors to try allocating in private color maps to minimize flashing */
63 #define NOFLASH_COLORS 256
65 /* architecture independent value <-> memory conversions;
66 note: the internal format is big endian */
68 #define memory_to_value(ptr, len) ( \
69 (len) == 1 ? (unsigned long)( *( (byte *)(ptr)) ) : \
70 (len) == 2 ? (unsigned long)(((unsigned long)(*( (byte *)(ptr)) ))<< 8) \
71 + ( *(((byte *)(ptr))+1) ) : \
72 (len) == 3 ? (unsigned long)(((unsigned long)(*( (byte *)(ptr)) ))<<16) \
73 + (((unsigned long)(*(((byte *)(ptr))+1)))<< 8) \
74 + ( *(((byte *)(ptr))+2) ) : \
75 (unsigned long)(((unsigned long)(*( (byte *)(ptr)) ))<<24) \
76 + (((unsigned long)(*(((byte *)(ptr))+1)))<<16) \
77 + (((unsigned long)(*(((byte *)(ptr))+2)))<< 8) \
78 + ( *(((byte *)(ptr))+3) ) )
81 #define value_to_memory(value, ptr, len) ( \
82 (len) == 1 ? (*( (byte *)(ptr) ) = ( value ) ) : \
83 (len) == 2 ? (*( (byte *)(ptr) ) = (((unsigned long)(value))>> 8), \
84 *(((byte *)(ptr))+1) = ( value ) ) : \
85 (len) == 3 ? (*( (byte *)(ptr) ) = (((unsigned long)(value))>>16), \
86 *(((byte *)(ptr))+1) = (((unsigned long)(value))>> 8), \
87 *(((byte *)(ptr))+2) = ( value ) ) : \
88 (*( (byte *)(ptr) ) = (((unsigned long)(value))>>24), \
89 *(((byte *)(ptr))+1) = (((unsigned long)(value))>>16), \
90 *(((byte *)(ptr))+2) = (((unsigned long)(value))>> 8), \
91 *(((byte *)(ptr))+3) = ( value ) ))
93 static Pixmap Image_to_Mask(Image *image, Display *display, Window window)
95 byte *src_ptr, *dst_ptr, *dst_ptr2;
96 unsigned int bytes_per_row;
102 bytes_per_row = (image->width + 7) / 8;
103 mask_data = checked_calloc(bytes_per_row * image->height);
105 src_ptr = image->data;
108 /* create bitmap data which can be used by 'XCreateBitmapFromData()'
109 * directly to create a pixmap of depth 1 for use as a clip mask for
110 * the corresponding image pixmap
113 for (y=0; y<image->height; y++)
115 bitmask = 0x01; /* start with leftmost bit in the byte */
116 dst_ptr2 = dst_ptr; /* start with leftmost byte in the row */
118 for (x=0; x<image->width; x++)
120 for (i=0; i<image->bytes_per_pixel; i++)
121 if (*src_ptr++) /* source pixel solid? (pixel index != 0) */
122 *dst_ptr2 |= bitmask; /* then write a bit into the image mask */
124 if ((bitmask <<= 1) == 0) /* bit at rightmost byte position reached? */
126 bitmask = 0x01; /* start again with leftmost bit position */
127 dst_ptr2++; /* continue with next byte in image mask */
131 dst_ptr += bytes_per_row; /* continue with leftmost byte of next row */
134 mask_pixmap = XCreateBitmapFromData(display, window, (char *)mask_data,
135 image->width, image->height);
141 static int bitsPerPixelAtDepth(Display *display, int screen, int depth)
143 XPixmapFormatValues *pixmap_format;
144 int i, num_pixmap_formats, bits_per_pixel = -1;
146 /* get Pixmap formats supported by the X server */
147 pixmap_format = XListPixmapFormats(display, &num_pixmap_formats);
149 /* find format that matches the given depth */
150 for (i=0; i<num_pixmap_formats; i++)
151 if (pixmap_format[i].depth == depth)
152 bits_per_pixel = pixmap_format[i].bits_per_pixel;
154 XFree(pixmap_format);
156 if (bits_per_pixel == -1)
157 Error(ERR_EXIT, "cannot find pixmap format for depth %d", depth);
159 return bits_per_pixel;
162 XImageInfo *Image_to_Pixmap(Display *display, int screen, Visual *visual,
163 Window window, GC gc, int depth, Image *image)
165 static XColor xcolor_private[NOFLASH_COLORS];
166 static int colorcell_used[NOFLASH_COLORS];
167 static Colormap global_cmap = 0;
168 static Pixel *global_cmap_index;
169 static int num_cmap_entries, free_cmap_entries;
170 static boolean private_cmap = FALSE;
171 Pixel *redvalue, *greenvalue, *bluevalue;
172 unsigned int display_bytes_per_pixel, display_bits_per_pixel;
173 unsigned int a, c = 0, x, y;
176 XImageInfo *ximageinfo;
177 byte *src_ptr, *dst_ptr;
178 char *error = "Image_to_Pixmap(): %s";
180 if (image->type == IMAGETYPE_TRUECOLOR && depth == 8)
182 SetError(error, "cannot handle true-color images on 8-bit display");
188 if (visual == DefaultVisual(display, screen))
189 global_cmap = DefaultColormap(display, screen);
192 global_cmap = XCreateColormap(display, RootWindow(display, screen),
198 xcolor.flags = DoRed | DoGreen | DoBlue;
199 redvalue = greenvalue = bluevalue = NULL;
200 ximageinfo = checked_malloc(sizeof(XImageInfo));
201 ximageinfo->display = display;
202 ximageinfo->depth = depth;
204 switch (visual->class)
210 unsigned int redcolors, greencolors, bluecolors;
211 unsigned int redstep, greenstep, bluestep;
212 unsigned int redbottom, greenbottom, bluebottom;
213 unsigned int redtop, greentop, bluetop;
215 redvalue = (Pixel *)checked_malloc(sizeof(Pixel) * 256);
216 greenvalue = (Pixel *)checked_malloc(sizeof(Pixel) * 256);
217 bluevalue = (Pixel *)checked_malloc(sizeof(Pixel) * 256);
219 ximageinfo->cmap = global_cmap;
221 retry_direct: /* tag we hit if a DirectColor allocation fails on
222 * default colormap */
224 /* calculate number of distinct colors in each band */
226 redcolors = greencolors = bluecolors = 1;
227 for (pixval=1; pixval; pixval <<= 1)
229 if (pixval & visual->red_mask)
231 if (pixval & visual->green_mask)
233 if (pixval & visual->blue_mask)
237 /* consistency check */
238 if (redcolors > visual->map_entries ||
239 greencolors > visual->map_entries ||
240 bluecolors > visual->map_entries)
241 Error(ERR_WARN, "inconsistency in color information");
243 redstep = 256 / redcolors;
244 greenstep = 256 / greencolors;
245 bluestep = 256 / bluecolors;
246 redbottom = greenbottom = bluebottom = 0;
247 redtop = greentop = bluetop = 0;
249 for (a=0; a<visual->map_entries; a++)
252 redtop = redbottom + redstep;
253 if (greenbottom < 256)
254 greentop = greenbottom + greenstep;
255 if (bluebottom < 256)
256 bluetop = bluebottom + bluestep;
258 xcolor.red = (redtop - 1) << 8;
259 xcolor.green = (greentop - 1) << 8;
260 xcolor.blue = (bluetop - 1) << 8;
261 if (!XAllocColor(display, ximageinfo->cmap, &xcolor))
263 /* if an allocation fails for a DirectColor default visual then
264 we should create a private colormap and try again. */
266 if ((visual->class == DirectColor) &&
267 (visual == DefaultVisual(display, screen)))
269 global_cmap = XCopyColormapAndFree(display, global_cmap);
270 ximageinfo->cmap = global_cmap;
276 /* something completely unexpected happened */
278 fprintf(stderr, "Image_to_Pixmap: XAllocColor failed on a TrueColor/Directcolor visual\n");
288 /* fill in pixel values for each band at this intensity */
290 while ((redbottom < 256) && (redbottom < redtop))
291 redvalue[redbottom++] = xcolor.pixel & visual->red_mask;
292 while ((greenbottom < 256) && (greenbottom < greentop))
293 greenvalue[greenbottom++] = xcolor.pixel & visual->green_mask;
294 while ((bluebottom < 256) && (bluebottom < bluetop))
295 bluevalue[bluebottom++] = xcolor.pixel & visual->blue_mask;
303 ximageinfo->cmap = global_cmap;
305 for (a=0; a<MAX_COLORS; a++)
312 if (!image->rgb.color_used[a])
315 xcolor.red = *(image->rgb.red + a);
316 xcolor.green = *(image->rgb.green + a);
317 xcolor.blue = *(image->rgb.blue + a);
319 /* look if this color already exists in our colormap */
320 if (!XAllocColor(display, ximageinfo->cmap, &xcolor))
325 Error(ERR_RETURN, "switching to private colormap");
327 /* we just filled up the default colormap -- get a private one
328 which contains all already allocated colors */
330 global_cmap = XCopyColormapAndFree(display, global_cmap);
331 ximageinfo->cmap = global_cmap;
334 /* allocate the rest of the color cells read/write */
336 (Pixel *)checked_malloc(sizeof(Pixel) * NOFLASH_COLORS);
337 for (i=0; i<NOFLASH_COLORS; i++)
338 if (!XAllocColorCells(display, global_cmap, FALSE, NULL, 0,
339 global_cmap_index + i, 1))
341 num_cmap_entries = free_cmap_entries = i;
344 printf("We've got %d free colormap entries.\n", free_cmap_entries);
347 /* to minimize colormap flashing, copy default colors and try
348 to keep them as near as possible to the old values */
350 for(i=0; i<num_cmap_entries; i++)
352 xcolor2.pixel = *(global_cmap_index + i);
353 XQueryColor(display, DefaultColormap(display, screen), &xcolor2);
354 XStoreColor(display, global_cmap, &xcolor2);
355 xcolor_private[xcolor2.pixel] = xcolor2;
356 colorcell_used[xcolor2.pixel] = FALSE;
359 /* now we have the default colormap private: all colors we
360 successfully allocated so far are read-only, which is okay,
361 because we don't want to change them anymore -- if we need
362 an existing color again, we get it by XAllocColor; all other
363 colors are read/write and we can set them by XStoreColor,
364 but we will try to overwrite those color cells with our new
365 color which are as close as possible to our new color */
368 /* look for an existing default color close the one we want */
375 for (i=num_cmap_entries-1; i>=0; i--)
377 xcolor2.pixel = *(global_cmap_index + i);
378 xcolor2 = xcolor_private[xcolor2.pixel];
380 if (colorcell_used[xcolor2.pixel])
383 if ((xcolor.red & mask) == (xcolor2.red & mask) &&
384 (xcolor.green & mask) == (xcolor2.green & mask) &&
385 (xcolor.blue & mask) == (xcolor2.blue & mask))
388 printf("replacing color cell %ld with a close color\n",
399 mask = (mask << 1) & 0xffff;
402 if (!color_found) /* no more free color cells */
404 SetError(error, "cannot allocate enough color cells");
408 xcolor.pixel = xcolor2.pixel;
409 xcolor_private[xcolor.pixel] = xcolor;
410 colorcell_used[xcolor.pixel] = TRUE;
411 XStoreColor(display, ximageinfo->cmap, &xcolor);
415 *(ximageinfo->index + a) = xcolor.pixel;
419 printf("still %d free colormap entries\n", free_cmap_entries);
422 ximageinfo->no = a; /* number of pixels allocated for this image */
426 Error(ERR_RETURN,"DirectColor, TrueColor or PseudoColor display needed");
427 SetError(error, "display class not supported");
433 debug_print_timestamp(2, " ALLOCATING IMAGE COLORS: ");
436 /* create XImage from internal image structure and convert it to Pixmap */
438 display_bits_per_pixel = bitsPerPixelAtDepth(display, screen, depth);
439 display_bytes_per_pixel = (display_bits_per_pixel + 7) / 8;
441 ximage = XCreateImage(display, visual, depth, ZPixmap, 0,
442 NULL, image->width, image->height,
443 8, image->width * display_bytes_per_pixel);
445 checked_malloc(image->width * image->height * display_bytes_per_pixel);
446 ximage->byte_order = MSBFirst;
448 src_ptr = image->data;
449 dst_ptr = (byte *)ximage->data;
451 switch (visual->class)
462 for (y=0; y<image->height; y++) /* general case */
464 for (x=0; x<image->width; x++)
468 redvalue[image->rgb.red[pixval] >> 8] |
469 greenvalue[image->rgb.green[pixval] >> 8] |
470 bluevalue[image->rgb.blue[pixval] >> 8];
471 value_to_memory(pixval, dst_ptr, display_bytes_per_pixel);
472 dst_ptr += display_bytes_per_pixel;
479 case IMAGETYPE_TRUECOLOR:
481 for (y=0; y<image->height; y++) /* general case */
483 for (x=0; x<image->width; x++)
485 pixval = memory_to_value(src_ptr, image->bytes_per_pixel);
487 redvalue[TRUECOLOR_RED(pixval)] |
488 greenvalue[TRUECOLOR_GREEN(pixval)] |
489 bluevalue[TRUECOLOR_BLUE(pixval)];
490 value_to_memory(pixval, dst_ptr, display_bytes_per_pixel);
491 src_ptr += image->bytes_per_pixel;
492 dst_ptr += display_bytes_per_pixel;
500 Error(ERR_RETURN, "RGB or TrueColor image needed");
501 SetError(error, "image type not supported");
511 if (display_bytes_per_pixel == 1) /* special case */
513 for (y=0; y<image->height; y++)
514 for (x=0; x<image->width; x++)
515 *dst_ptr++ = ximageinfo->index[c + *src_ptr++];
517 else /* general case */
519 for (y=0; y<image->height; y++)
521 for (x=0; x<image->width; x++)
523 value_to_memory(ximageinfo->index[c + *src_ptr++],
524 dst_ptr, display_bytes_per_pixel);
525 dst_ptr += display_bytes_per_pixel;
534 Error(ERR_RETURN,"DirectColor, TrueColor or PseudoColor display needed");
535 SetError(error, "display class not supported");
542 free((byte *)redvalue);
543 free((byte *)greenvalue);
544 free((byte *)bluevalue);
548 debug_print_timestamp(2, " CONVERTING IMAGE TO XIMAGE:");
551 ximageinfo->pixmap = XCreatePixmap(display, window,
552 ximage->width, ximage->height,
555 XPutImage(ximageinfo->display, ximageinfo->pixmap, gc,
556 ximage, 0, 0, 0, 0, ximage->width, ximage->height);
558 XDestroyImage(ximage);
563 void freeXImage(Image *image, XImageInfo *ximageinfo)
565 if (ximageinfo->index != NULL && ximageinfo->no > 0)
566 XFreeColors(ximageinfo->display, ximageinfo->cmap, ximageinfo->index,
568 /* this ^^^^^^^^^^^^^^ is wrong, because the used color cells
569 * are somewhere between 0 and MAX_COLORS; there are indeed 'ximageinfo->no'
570 * used color cells, but they are not at array position 0 - 'ximageinfo->no'
576 int Read_PCX_to_Pixmap(Display *display, Window window, GC gc, char *filename,
577 Pixmap *pixmap, Pixmap *pixmap_mask)
580 XImageInfo *ximageinfo;
586 debug_print_timestamp(2, NULL); /* initialize timestamp function */
589 /* read the graphic file in PCX format to image structure */
590 if ((image = Read_PCX_to_Image(filename)) == NULL)
594 printf("%s:\n", filename);
595 debug_print_timestamp(2, " READING PCX FILE TO IMAGE: ");
598 screen = DefaultScreen(display);
599 visual = DefaultVisual(display, screen);
600 depth = DefaultDepth(display, screen);
602 /* convert image structure to X11 Pixmap */
603 if (!(ximageinfo = Image_to_Pixmap(display, screen, visual,
604 window, gc, depth, image)))
608 return PCX_OtherError;
611 /* if a private colormap has been created, install it */
612 if (ximageinfo->cmap != DefaultColormap(display, screen))
613 XSetWindowColormap(display, window, ximageinfo->cmap);
616 debug_print_timestamp(2, " CONVERTING IMAGE TO PIXMAP:");
619 /* create clip mask for the image */
620 ximageinfo->pixmap_mask = Image_to_Mask(image, display, window);
623 debug_print_timestamp(2, " CONVERTING IMAGE TO MASK: ");
626 *pixmap = ximageinfo->pixmap;
627 *pixmap_mask = ximageinfo->pixmap_mask;
629 /* free generic image and ximageinfo after native Pixmap has been created */
636 #endif /* PLATFORM_UNIX */
637 #endif /* TARGET_X11 */