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 const unsigned int bytes_per_pixel = 1;
30 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;
41 for (i=0; i<MAX_COLORS; i++)
42 image->rgb.color_used[i] = FALSE;
44 image->type = (depth < 8 ? IMAGETYPE_BITMAP :
45 depth > 8 ? IMAGETYPE_TRUECOLOR : IMAGETYPE_RGB);
47 image->bytes_per_row = (depth + 7) / 8;
48 image->bytes_per_row *= width;
53 void freeImage(Image *image)
59 #if defined(PLATFORM_UNIX)
61 /* extra colors to try allocating in private color maps to minimize flashing */
62 #define NOFLASH_COLORS 256
64 /* architecture independent value <-> memory conversions;
65 note: the internal format is big endian */
67 #define memory_to_value(ptr, len) ( \
68 (len) == 1 ? (unsigned long)( *( (byte *)(ptr)) ) : \
69 (len) == 2 ? (unsigned long)(((unsigned long)(*( (byte *)(ptr)) ))<< 8) \
70 + ( *(((byte *)(ptr))+1) ) : \
71 (len) == 3 ? (unsigned long)(((unsigned long)(*( (byte *)(ptr)) ))<<16) \
72 + (((unsigned long)(*(((byte *)(ptr))+1)))<< 8) \
73 + ( *(((byte *)(ptr))+2) ) : \
74 (unsigned long)(((unsigned long)(*( (byte *)(ptr)) ))<<24) \
75 + (((unsigned long)(*(((byte *)(ptr))+1)))<<16) \
76 + (((unsigned long)(*(((byte *)(ptr))+2)))<< 8) \
77 + ( *(((byte *)(ptr))+3) ) )
80 #define value_to_memory(value, ptr, len) ( \
81 (len) == 1 ? (*( (byte *)(ptr) ) = ( value ) ) : \
82 (len) == 2 ? (*( (byte *)(ptr) ) = (((unsigned long)(value))>> 8), \
83 *(((byte *)(ptr))+1) = ( value ) ) : \
84 (len) == 3 ? (*( (byte *)(ptr) ) = (((unsigned long)(value))>>16), \
85 *(((byte *)(ptr))+1) = (((unsigned long)(value))>> 8), \
86 *(((byte *)(ptr))+2) = ( value ) ) : \
87 (*( (byte *)(ptr) ) = (((unsigned long)(value))>>24), \
88 *(((byte *)(ptr))+1) = (((unsigned long)(value))>>16), \
89 *(((byte *)(ptr))+2) = (((unsigned long)(value))>> 8), \
90 *(((byte *)(ptr))+3) = ( value ) ))
92 static Pixmap Image_to_Mask(Image *image, Display *display, Window window)
94 byte *src_ptr, *dst_ptr, *dst_ptr2;
95 unsigned int bytes_per_row;
101 bytes_per_row = (image->width + 7) / 8;
102 mask_data = checked_calloc(bytes_per_row * image->height);
104 src_ptr = image->data;
107 /* create bitmap data which can be used by 'XCreateBitmapFromData()'
108 * directly to create a pixmap of depth 1 for use as a clip mask for
109 * the corresponding image pixmap
112 for (y=0; y<image->height; y++)
114 bitmask = 0x01; /* start with leftmost bit in the byte */
115 dst_ptr2 = dst_ptr; /* start with leftmost byte in the row */
117 for (x=0; x<image->width; x++)
119 if (*src_ptr++) /* source pixel solid? (pixel index != 0) */
120 *dst_ptr2 |= bitmask; /* then write a bit into the image mask */
122 if ((bitmask <<= 1) == 0) /* bit at rightmost byte position reached? */
124 bitmask = 0x01; /* start again with leftmost bit position */
125 dst_ptr2++; /* continue with next byte in image mask */
129 dst_ptr += bytes_per_row; /* continue with leftmost byte of next row */
132 mask_pixmap = XCreateBitmapFromData(display, window, (char *)mask_data,
133 image->width, image->height);
139 static int bitsPerPixelAtDepth(Display *display, int screen, int depth)
141 XPixmapFormatValues *pixmap_format;
142 int i, num_pixmap_formats, bits_per_pixel = -1;
144 /* get Pixmap formats supported by the X server */
145 pixmap_format = XListPixmapFormats(display, &num_pixmap_formats);
147 /* find format that matches the given depth */
148 for (i=0; i<num_pixmap_formats; i++)
149 if (pixmap_format[i].depth == depth)
150 bits_per_pixel = pixmap_format[i].bits_per_pixel;
152 XFree(pixmap_format);
154 if (bits_per_pixel == -1)
155 Error(ERR_EXIT, "cannot find pixmap format for depth %d", depth);
157 return bits_per_pixel;
160 XImageInfo *Image_to_Pixmap(Display *display, int screen, Visual *visual,
161 Window window, GC gc, int depth, Image *image)
163 static XColor xcolor_private[NOFLASH_COLORS];
164 static int colorcell_used[NOFLASH_COLORS];
165 static Colormap global_cmap = 0;
166 static Pixel *global_cmap_index;
167 static int num_cmap_entries, free_cmap_entries;
168 static boolean private_cmap = FALSE;
169 Pixel *redvalue, *greenvalue, *bluevalue;
170 unsigned int a, c = 0, x, y, bytes_per_pixel, bits_per_pixel;
173 XImageInfo *ximageinfo;
174 byte *src_ptr, *dst_ptr;
178 if (visual == DefaultVisual(display, screen))
179 global_cmap = DefaultColormap(display, screen);
182 global_cmap = XCreateColormap(display, RootWindow(display, screen),
188 xcolor.flags = DoRed | DoGreen | DoBlue;
189 redvalue = greenvalue = bluevalue = NULL;
190 ximageinfo = checked_malloc(sizeof(XImageInfo));
191 ximageinfo->display = display;
192 ximageinfo->depth = depth;
194 switch (visual->class)
200 unsigned int redcolors, greencolors, bluecolors;
201 unsigned int redstep, greenstep, bluestep;
202 unsigned int redbottom, greenbottom, bluebottom;
203 unsigned int redtop, greentop, bluetop;
205 redvalue = (Pixel *)checked_malloc(sizeof(Pixel) * 256);
206 greenvalue = (Pixel *)checked_malloc(sizeof(Pixel) * 256);
207 bluevalue = (Pixel *)checked_malloc(sizeof(Pixel) * 256);
209 ximageinfo->cmap = global_cmap;
211 retry_direct: /* tag we hit if a DirectColor allocation fails on
212 * default colormap */
214 /* calculate number of distinct colors in each band */
216 redcolors = greencolors = bluecolors = 1;
217 for (pixval=1; pixval; pixval <<= 1)
219 if (pixval & visual->red_mask)
221 if (pixval & visual->green_mask)
223 if (pixval & visual->blue_mask)
227 /* consistency check */
228 if (redcolors > visual->map_entries ||
229 greencolors > visual->map_entries ||
230 bluecolors > visual->map_entries)
231 Error(ERR_WARN, "inconsistency in color information");
233 redstep = 256 / redcolors;
234 greenstep = 256 / greencolors;
235 bluestep = 256 / bluecolors;
236 redbottom = greenbottom = bluebottom = 0;
237 redtop = greentop = bluetop = 0;
238 for (a=0; a<visual->map_entries; a++)
241 redtop = redbottom + redstep;
242 if (greenbottom < 256)
243 greentop = greenbottom + greenstep;
244 if (bluebottom < 256)
245 bluetop = bluebottom + bluestep;
247 xcolor.red = (redtop - 1) << 8;
248 xcolor.green = (greentop - 1) << 8;
249 xcolor.blue = (bluetop - 1) << 8;
250 if (!XAllocColor(display, ximageinfo->cmap, &xcolor))
252 /* if an allocation fails for a DirectColor default visual then
253 we should create a private colormap and try again. */
255 if ((visual->class == DirectColor) &&
256 (visual == DefaultVisual(display, screen)))
258 global_cmap = XCopyColormapAndFree(display, global_cmap);
259 ximageinfo->cmap = global_cmap;
265 /* something completely unexpected happened */
267 fprintf(stderr, "Image_to_Pixmap: XAllocColor failed on a TrueColor/Directcolor visual\n");
275 /* fill in pixel values for each band at this intensity */
277 while ((redbottom < 256) && (redbottom < redtop))
278 redvalue[redbottom++] = xcolor.pixel & visual->red_mask;
279 while ((greenbottom < 256) && (greenbottom < greentop))
280 greenvalue[greenbottom++] = xcolor.pixel & visual->green_mask;
281 while ((bluebottom < 256) && (bluebottom < bluetop))
282 bluevalue[bluebottom++] = xcolor.pixel & visual->blue_mask;
289 ximageinfo->cmap = global_cmap;
291 for (a=0; a<MAX_COLORS; a++)
298 if (!image->rgb.color_used[a])
301 xcolor.red = *(image->rgb.red + a);
302 xcolor.green = *(image->rgb.green + a);
303 xcolor.blue = *(image->rgb.blue + a);
305 /* look if this color already exists in our colormap */
306 if (!XAllocColor(display, ximageinfo->cmap, &xcolor))
311 Error(ERR_RETURN, "switching to private colormap");
313 /* we just filled up the default colormap -- get a private one
314 which contains all already allocated colors */
316 global_cmap = XCopyColormapAndFree(display, global_cmap);
317 ximageinfo->cmap = global_cmap;
320 /* allocate the rest of the color cells read/write */
322 (Pixel *)checked_malloc(sizeof(Pixel) * NOFLASH_COLORS);
323 for (i=0; i<NOFLASH_COLORS; i++)
324 if (!XAllocColorCells(display, global_cmap, FALSE, NULL, 0,
325 global_cmap_index + i, 1))
327 num_cmap_entries = free_cmap_entries = i;
330 printf("We've got %d free colormap entries.\n", free_cmap_entries);
333 /* to minimize colormap flashing, copy default colors and try
334 to keep them as near as possible to the old values */
336 for(i=0; i<num_cmap_entries; i++)
338 xcolor2.pixel = *(global_cmap_index + i);
339 XQueryColor(display, DefaultColormap(display, screen), &xcolor2);
340 XStoreColor(display, global_cmap, &xcolor2);
341 xcolor_private[xcolor2.pixel] = xcolor2;
342 colorcell_used[xcolor2.pixel] = FALSE;
345 /* now we have the default colormap private: all colors we
346 successfully allocated so far are read-only, which is okay,
347 because we don't want to change them anymore -- if we need
348 an existing color again, we get it by XAllocColor; all other
349 colors are read/write and we can set them by XStoreColor,
350 but we will try to overwrite those color cells with our new
351 color which are as close as possible to our new color */
354 /* look for an existing default color close the one we want */
361 for (i=num_cmap_entries-1; i>=0; i--)
363 xcolor2.pixel = *(global_cmap_index + i);
364 xcolor2 = xcolor_private[xcolor2.pixel];
366 if (colorcell_used[xcolor2.pixel])
369 if ((xcolor.red & mask) == (xcolor2.red & mask) &&
370 (xcolor.green & mask) == (xcolor2.green & mask) &&
371 (xcolor.blue & mask) == (xcolor2.blue & mask))
374 printf("replacing color cell %ld with a close color\n",
385 mask = (mask << 1) & 0xffff;
388 if (!color_found) /* no more free color cells */
389 Error(ERR_EXIT, "cannot allocate enough color cells");
391 xcolor.pixel = xcolor2.pixel;
392 xcolor_private[xcolor.pixel] = xcolor;
393 colorcell_used[xcolor.pixel] = TRUE;
394 XStoreColor(display, ximageinfo->cmap, &xcolor);
398 *(ximageinfo->index + a) = xcolor.pixel;
402 printf("still %d free colormap entries\n", free_cmap_entries);
405 ximageinfo->no = a; /* number of pixels allocated for this image */
409 Error(ERR_RETURN, "display class not supported");
410 Error(ERR_EXIT, "DirectColor, TrueColor or PseudoColor display needed");
415 debug_print_timestamp(2, " ALLOCATING IMAGE COLORS: ");
418 /* create XImage from internal image structure and convert it to Pixmap */
420 bits_per_pixel = bitsPerPixelAtDepth(display, screen, depth);
421 bytes_per_pixel = (bits_per_pixel + 7) / 8;
423 ximage = XCreateImage(display, visual, depth, ZPixmap, 0,
424 NULL, image->width, image->height,
425 8, image->width * bytes_per_pixel);
427 checked_malloc(image->width * image->height * bytes_per_pixel);
428 ximage->byte_order = MSBFirst;
430 src_ptr = image->data;
431 dst_ptr = (byte *)ximage->data;
433 switch (visual->class)
444 for (y=0; y<image->height; y++) /* general case */
446 for (x=0; x<image->width; x++)
450 redvalue[image->rgb.red[pixval] >> 8] |
451 greenvalue[image->rgb.green[pixval] >> 8] |
452 bluevalue[image->rgb.blue[pixval] >> 8];
453 value_to_memory(pixval, dst_ptr, bytes_per_pixel);
454 dst_ptr += bytes_per_pixel;
460 case IMAGETYPE_TRUECOLOR:
462 for (y=0; y<image->height; y++) /* general case */
464 for (x=0; x<image->width; x++)
466 pixval = memory_to_value(src_ptr, image->depth);
468 redvalue[TRUECOLOR_RED(pixval)] |
469 greenvalue[TRUECOLOR_GREEN(pixval)] |
470 bluevalue[TRUECOLOR_BLUE(pixval)];
471 value_to_memory(pixval, dst_ptr, bytes_per_pixel);
472 src_ptr += image->depth;
473 dst_ptr += bytes_per_pixel;
480 Error(ERR_RETURN, "image type not supported");
481 Error(ERR_EXIT, "RGB or TrueColor image needed");
489 if (bytes_per_pixel == 1) /* (common) special case */
491 for (y=0; y<image->height; y++)
492 for (x=0; x<image->width; x++)
493 *dst_ptr++ = ximageinfo->index[c + *src_ptr++];
495 else /* general case */
497 for (y=0; y<image->height; y++)
499 for (x=0; x<image->width; x++)
501 value_to_memory(ximageinfo->index[c + *src_ptr++],
502 dst_ptr, bytes_per_pixel);
503 dst_ptr += bytes_per_pixel;
511 Error(ERR_RETURN, "display class not supported");
512 Error(ERR_EXIT, "DirectColor, TrueColor or PseudoColor display needed");
518 free((byte *)redvalue);
519 free((byte *)greenvalue);
520 free((byte *)bluevalue);
524 debug_print_timestamp(2, " CONVERTING IMAGE TO XIMAGE:");
527 ximageinfo->pixmap = XCreatePixmap(display, window,
528 ximage->width, ximage->height,
531 XPutImage(ximageinfo->display, ximageinfo->pixmap, gc,
532 ximage, 0, 0, 0, 0, ximage->width, ximage->height);
536 XDestroyImage(ximage);
541 void freeXImage(Image *image, XImageInfo *ximageinfo)
543 if (ximageinfo->index != NULL && ximageinfo->no > 0)
544 XFreeColors(ximageinfo->display, ximageinfo->cmap, ximageinfo->index,
546 /* this ^^^^^^^^^^^^^^ is wrong, because the used color cells
547 * are somewhere between 0 and MAX_COLORS; there are indeed 'ximageinfo->no'
548 * used color cells, but they are not at array position 0 - 'ximageinfo->no'
554 int Read_PCX_to_Pixmap(Display *display, Window window, GC gc, char *filename,
555 Pixmap *pixmap, Pixmap *pixmap_mask)
558 XImageInfo *ximageinfo;
564 debug_print_timestamp(2, NULL); /* initialize timestamp function */
567 /* read the graphic file in PCX format to image structure */
568 if ((image = Read_PCX_to_Image(filename)) == NULL)
572 printf("%s:\n", filename);
573 debug_print_timestamp(2, " READING PCX FILE TO IMAGE: ");
576 screen = DefaultScreen(display);
577 visual = DefaultVisual(display, screen);
578 depth = DefaultDepth(display, screen);
580 /* convert image structure to X11 Pixmap */
581 if (!(ximageinfo = Image_to_Pixmap(display, screen, visual,
582 window, gc, depth, image)))
583 Error(ERR_EXIT, "cannot convert Image to Pixmap");
585 /* if a private colormap has been created, install it */
586 if (ximageinfo->cmap != DefaultColormap(display, screen))
587 XSetWindowColormap(display, window, ximageinfo->cmap);
590 debug_print_timestamp(2, " CONVERTING IMAGE TO PIXMAP:");
593 /* create clip mask for the image */
594 ximageinfo->pixmap_mask = Image_to_Mask(image, display, window);
597 debug_print_timestamp(2, " CONVERTING IMAGE TO MASK: ");
600 *pixmap = ximageinfo->pixmap;
601 *pixmap_mask = ximageinfo->pixmap_mask;
606 #endif /* PLATFORM_UNIX */
607 #endif /* TARGET_X11 */