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;
179 if (image->type == IMAGETYPE_TRUECOLOR && depth == 8)
180 Error(ERR_EXIT, "cannot handle true-color images on 8-bit display");
184 if (visual == DefaultVisual(display, screen))
185 global_cmap = DefaultColormap(display, screen);
188 global_cmap = XCreateColormap(display, RootWindow(display, screen),
194 xcolor.flags = DoRed | DoGreen | DoBlue;
195 redvalue = greenvalue = bluevalue = NULL;
196 ximageinfo = checked_malloc(sizeof(XImageInfo));
197 ximageinfo->display = display;
198 ximageinfo->depth = depth;
200 switch (visual->class)
206 unsigned int redcolors, greencolors, bluecolors;
207 unsigned int redstep, greenstep, bluestep;
208 unsigned int redbottom, greenbottom, bluebottom;
209 unsigned int redtop, greentop, bluetop;
211 redvalue = (Pixel *)checked_malloc(sizeof(Pixel) * 256);
212 greenvalue = (Pixel *)checked_malloc(sizeof(Pixel) * 256);
213 bluevalue = (Pixel *)checked_malloc(sizeof(Pixel) * 256);
215 ximageinfo->cmap = global_cmap;
217 retry_direct: /* tag we hit if a DirectColor allocation fails on
218 * default colormap */
220 /* calculate number of distinct colors in each band */
222 redcolors = greencolors = bluecolors = 1;
223 for (pixval=1; pixval; pixval <<= 1)
225 if (pixval & visual->red_mask)
227 if (pixval & visual->green_mask)
229 if (pixval & visual->blue_mask)
233 /* consistency check */
234 if (redcolors > visual->map_entries ||
235 greencolors > visual->map_entries ||
236 bluecolors > visual->map_entries)
237 Error(ERR_WARN, "inconsistency in color information");
239 redstep = 256 / redcolors;
240 greenstep = 256 / greencolors;
241 bluestep = 256 / bluecolors;
242 redbottom = greenbottom = bluebottom = 0;
243 redtop = greentop = bluetop = 0;
244 for (a=0; a<visual->map_entries; a++)
247 redtop = redbottom + redstep;
248 if (greenbottom < 256)
249 greentop = greenbottom + greenstep;
250 if (bluebottom < 256)
251 bluetop = bluebottom + bluestep;
253 xcolor.red = (redtop - 1) << 8;
254 xcolor.green = (greentop - 1) << 8;
255 xcolor.blue = (bluetop - 1) << 8;
256 if (!XAllocColor(display, ximageinfo->cmap, &xcolor))
258 /* if an allocation fails for a DirectColor default visual then
259 we should create a private colormap and try again. */
261 if ((visual->class == DirectColor) &&
262 (visual == DefaultVisual(display, screen)))
264 global_cmap = XCopyColormapAndFree(display, global_cmap);
265 ximageinfo->cmap = global_cmap;
271 /* something completely unexpected happened */
273 fprintf(stderr, "Image_to_Pixmap: XAllocColor failed on a TrueColor/Directcolor visual\n");
281 /* fill in pixel values for each band at this intensity */
283 while ((redbottom < 256) && (redbottom < redtop))
284 redvalue[redbottom++] = xcolor.pixel & visual->red_mask;
285 while ((greenbottom < 256) && (greenbottom < greentop))
286 greenvalue[greenbottom++] = xcolor.pixel & visual->green_mask;
287 while ((bluebottom < 256) && (bluebottom < bluetop))
288 bluevalue[bluebottom++] = xcolor.pixel & visual->blue_mask;
295 ximageinfo->cmap = global_cmap;
297 for (a=0; a<MAX_COLORS; a++)
304 if (!image->rgb.color_used[a])
307 xcolor.red = *(image->rgb.red + a);
308 xcolor.green = *(image->rgb.green + a);
309 xcolor.blue = *(image->rgb.blue + a);
311 /* look if this color already exists in our colormap */
312 if (!XAllocColor(display, ximageinfo->cmap, &xcolor))
317 Error(ERR_RETURN, "switching to private colormap");
319 /* we just filled up the default colormap -- get a private one
320 which contains all already allocated colors */
322 global_cmap = XCopyColormapAndFree(display, global_cmap);
323 ximageinfo->cmap = global_cmap;
326 /* allocate the rest of the color cells read/write */
328 (Pixel *)checked_malloc(sizeof(Pixel) * NOFLASH_COLORS);
329 for (i=0; i<NOFLASH_COLORS; i++)
330 if (!XAllocColorCells(display, global_cmap, FALSE, NULL, 0,
331 global_cmap_index + i, 1))
333 num_cmap_entries = free_cmap_entries = i;
336 printf("We've got %d free colormap entries.\n", free_cmap_entries);
339 /* to minimize colormap flashing, copy default colors and try
340 to keep them as near as possible to the old values */
342 for(i=0; i<num_cmap_entries; i++)
344 xcolor2.pixel = *(global_cmap_index + i);
345 XQueryColor(display, DefaultColormap(display, screen), &xcolor2);
346 XStoreColor(display, global_cmap, &xcolor2);
347 xcolor_private[xcolor2.pixel] = xcolor2;
348 colorcell_used[xcolor2.pixel] = FALSE;
351 /* now we have the default colormap private: all colors we
352 successfully allocated so far are read-only, which is okay,
353 because we don't want to change them anymore -- if we need
354 an existing color again, we get it by XAllocColor; all other
355 colors are read/write and we can set them by XStoreColor,
356 but we will try to overwrite those color cells with our new
357 color which are as close as possible to our new color */
360 /* look for an existing default color close the one we want */
367 for (i=num_cmap_entries-1; i>=0; i--)
369 xcolor2.pixel = *(global_cmap_index + i);
370 xcolor2 = xcolor_private[xcolor2.pixel];
372 if (colorcell_used[xcolor2.pixel])
375 if ((xcolor.red & mask) == (xcolor2.red & mask) &&
376 (xcolor.green & mask) == (xcolor2.green & mask) &&
377 (xcolor.blue & mask) == (xcolor2.blue & mask))
380 printf("replacing color cell %ld with a close color\n",
391 mask = (mask << 1) & 0xffff;
394 if (!color_found) /* no more free color cells */
395 Error(ERR_EXIT, "cannot allocate enough color cells");
397 xcolor.pixel = xcolor2.pixel;
398 xcolor_private[xcolor.pixel] = xcolor;
399 colorcell_used[xcolor.pixel] = TRUE;
400 XStoreColor(display, ximageinfo->cmap, &xcolor);
404 *(ximageinfo->index + a) = xcolor.pixel;
408 printf("still %d free colormap entries\n", free_cmap_entries);
411 ximageinfo->no = a; /* number of pixels allocated for this image */
415 Error(ERR_RETURN, "display class not supported");
416 Error(ERR_EXIT, "DirectColor, TrueColor or PseudoColor display needed");
421 debug_print_timestamp(2, " ALLOCATING IMAGE COLORS: ");
424 /* create XImage from internal image structure and convert it to Pixmap */
426 display_bits_per_pixel = bitsPerPixelAtDepth(display, screen, depth);
427 display_bytes_per_pixel = (display_bits_per_pixel + 7) / 8;
429 ximage = XCreateImage(display, visual, depth, ZPixmap, 0,
430 NULL, image->width, image->height,
431 8, image->width * display_bytes_per_pixel);
433 checked_malloc(image->width * image->height * display_bytes_per_pixel);
434 ximage->byte_order = MSBFirst;
436 src_ptr = image->data;
437 dst_ptr = (byte *)ximage->data;
439 switch (visual->class)
450 for (y=0; y<image->height; y++) /* general case */
452 for (x=0; x<image->width; x++)
456 redvalue[image->rgb.red[pixval] >> 8] |
457 greenvalue[image->rgb.green[pixval] >> 8] |
458 bluevalue[image->rgb.blue[pixval] >> 8];
459 value_to_memory(pixval, dst_ptr, display_bytes_per_pixel);
460 dst_ptr += display_bytes_per_pixel;
466 case IMAGETYPE_TRUECOLOR:
468 for (y=0; y<image->height; y++) /* general case */
470 for (x=0; x<image->width; x++)
472 pixval = memory_to_value(src_ptr, image->bytes_per_pixel);
474 redvalue[TRUECOLOR_RED(pixval)] |
475 greenvalue[TRUECOLOR_GREEN(pixval)] |
476 bluevalue[TRUECOLOR_BLUE(pixval)];
477 value_to_memory(pixval, dst_ptr, display_bytes_per_pixel);
478 src_ptr += image->bytes_per_pixel;
479 dst_ptr += display_bytes_per_pixel;
486 Error(ERR_RETURN, "image type not supported");
487 Error(ERR_EXIT, "RGB or TrueColor image needed");
495 if (display_bytes_per_pixel == 1) /* special case */
497 for (y=0; y<image->height; y++)
498 for (x=0; x<image->width; x++)
499 *dst_ptr++ = ximageinfo->index[c + *src_ptr++];
501 else /* general case */
503 for (y=0; y<image->height; y++)
505 for (x=0; x<image->width; x++)
507 value_to_memory(ximageinfo->index[c + *src_ptr++],
508 dst_ptr, display_bytes_per_pixel);
509 dst_ptr += display_bytes_per_pixel;
517 Error(ERR_RETURN, "display class not supported");
518 Error(ERR_EXIT, "DirectColor, TrueColor or PseudoColor display needed");
524 free((byte *)redvalue);
525 free((byte *)greenvalue);
526 free((byte *)bluevalue);
530 debug_print_timestamp(2, " CONVERTING IMAGE TO XIMAGE:");
533 ximageinfo->pixmap = XCreatePixmap(display, window,
534 ximage->width, ximage->height,
537 XPutImage(ximageinfo->display, ximageinfo->pixmap, gc,
538 ximage, 0, 0, 0, 0, ximage->width, ximage->height);
542 XDestroyImage(ximage);
547 void freeXImage(Image *image, XImageInfo *ximageinfo)
549 if (ximageinfo->index != NULL && ximageinfo->no > 0)
550 XFreeColors(ximageinfo->display, ximageinfo->cmap, ximageinfo->index,
552 /* this ^^^^^^^^^^^^^^ is wrong, because the used color cells
553 * are somewhere between 0 and MAX_COLORS; there are indeed 'ximageinfo->no'
554 * used color cells, but they are not at array position 0 - 'ximageinfo->no'
560 int Read_PCX_to_Pixmap(Display *display, Window window, GC gc, char *filename,
561 Pixmap *pixmap, Pixmap *pixmap_mask)
564 XImageInfo *ximageinfo;
570 debug_print_timestamp(2, NULL); /* initialize timestamp function */
573 /* read the graphic file in PCX format to image structure */
574 if ((image = Read_PCX_to_Image(filename)) == NULL)
578 printf("%s:\n", filename);
579 debug_print_timestamp(2, " READING PCX FILE TO IMAGE: ");
582 screen = DefaultScreen(display);
583 visual = DefaultVisual(display, screen);
584 depth = DefaultDepth(display, screen);
586 /* convert image structure to X11 Pixmap */
587 if (!(ximageinfo = Image_to_Pixmap(display, screen, visual,
588 window, gc, depth, image)))
589 Error(ERR_EXIT, "cannot convert Image to Pixmap");
591 /* if a private colormap has been created, install it */
592 if (ximageinfo->cmap != DefaultColormap(display, screen))
593 XSetWindowColormap(display, window, ximageinfo->cmap);
596 debug_print_timestamp(2, " CONVERTING IMAGE TO PIXMAP:");
599 /* create clip mask for the image */
600 ximageinfo->pixmap_mask = Image_to_Mask(image, display, window);
603 debug_print_timestamp(2, " CONVERTING IMAGE TO MASK: ");
606 *pixmap = ximageinfo->pixmap;
607 *pixmap_mask = ximageinfo->pixmap_mask;
612 #endif /* PLATFORM_UNIX */
613 #endif /* TARGET_X11 */