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;
248 for (a=0; a<visual->map_entries; a++)
251 redtop = redbottom + redstep;
252 if (greenbottom < 256)
253 greentop = greenbottom + greenstep;
254 if (bluebottom < 256)
255 bluetop = bluebottom + bluestep;
257 xcolor.red = (redtop - 1) << 8;
258 xcolor.green = (greentop - 1) << 8;
259 xcolor.blue = (bluetop - 1) << 8;
260 if (!XAllocColor(display, ximageinfo->cmap, &xcolor))
262 /* if an allocation fails for a DirectColor default visual then
263 we should create a private colormap and try again. */
265 if ((visual->class == DirectColor) &&
266 (visual == DefaultVisual(display, screen)))
268 global_cmap = XCopyColormapAndFree(display, global_cmap);
269 ximageinfo->cmap = global_cmap;
275 /* something completely unexpected happened */
277 fprintf(stderr, "Image_to_Pixmap: XAllocColor failed on a TrueColor/Directcolor visual\n");
285 /* fill in pixel values for each band at this intensity */
287 while ((redbottom < 256) && (redbottom < redtop))
288 redvalue[redbottom++] = xcolor.pixel & visual->red_mask;
289 while ((greenbottom < 256) && (greenbottom < greentop))
290 greenvalue[greenbottom++] = xcolor.pixel & visual->green_mask;
291 while ((bluebottom < 256) && (bluebottom < bluetop))
292 bluevalue[bluebottom++] = xcolor.pixel & visual->blue_mask;
299 ximageinfo->cmap = global_cmap;
301 for (a=0; a<MAX_COLORS; a++)
308 if (!image->rgb.color_used[a])
311 xcolor.red = *(image->rgb.red + a);
312 xcolor.green = *(image->rgb.green + a);
313 xcolor.blue = *(image->rgb.blue + a);
315 /* look if this color already exists in our colormap */
316 if (!XAllocColor(display, ximageinfo->cmap, &xcolor))
321 Error(ERR_RETURN, "switching to private colormap");
323 /* we just filled up the default colormap -- get a private one
324 which contains all already allocated colors */
326 global_cmap = XCopyColormapAndFree(display, global_cmap);
327 ximageinfo->cmap = global_cmap;
330 /* allocate the rest of the color cells read/write */
332 (Pixel *)checked_malloc(sizeof(Pixel) * NOFLASH_COLORS);
333 for (i=0; i<NOFLASH_COLORS; i++)
334 if (!XAllocColorCells(display, global_cmap, FALSE, NULL, 0,
335 global_cmap_index + i, 1))
337 num_cmap_entries = free_cmap_entries = i;
340 printf("We've got %d free colormap entries.\n", free_cmap_entries);
343 /* to minimize colormap flashing, copy default colors and try
344 to keep them as near as possible to the old values */
346 for(i=0; i<num_cmap_entries; i++)
348 xcolor2.pixel = *(global_cmap_index + i);
349 XQueryColor(display, DefaultColormap(display, screen), &xcolor2);
350 XStoreColor(display, global_cmap, &xcolor2);
351 xcolor_private[xcolor2.pixel] = xcolor2;
352 colorcell_used[xcolor2.pixel] = FALSE;
355 /* now we have the default colormap private: all colors we
356 successfully allocated so far are read-only, which is okay,
357 because we don't want to change them anymore -- if we need
358 an existing color again, we get it by XAllocColor; all other
359 colors are read/write and we can set them by XStoreColor,
360 but we will try to overwrite those color cells with our new
361 color which are as close as possible to our new color */
364 /* look for an existing default color close the one we want */
371 for (i=num_cmap_entries-1; i>=0; i--)
373 xcolor2.pixel = *(global_cmap_index + i);
374 xcolor2 = xcolor_private[xcolor2.pixel];
376 if (colorcell_used[xcolor2.pixel])
379 if ((xcolor.red & mask) == (xcolor2.red & mask) &&
380 (xcolor.green & mask) == (xcolor2.green & mask) &&
381 (xcolor.blue & mask) == (xcolor2.blue & mask))
384 printf("replacing color cell %ld with a close color\n",
395 mask = (mask << 1) & 0xffff;
398 if (!color_found) /* no more free color cells */
400 SetError(error, "cannot allocate enough color cells");
404 xcolor.pixel = xcolor2.pixel;
405 xcolor_private[xcolor.pixel] = xcolor;
406 colorcell_used[xcolor.pixel] = TRUE;
407 XStoreColor(display, ximageinfo->cmap, &xcolor);
411 *(ximageinfo->index + a) = xcolor.pixel;
415 printf("still %d free colormap entries\n", free_cmap_entries);
418 ximageinfo->no = a; /* number of pixels allocated for this image */
422 Error(ERR_RETURN,"DirectColor, TrueColor or PseudoColor display needed");
423 SetError(error, "display class not supported");
428 debug_print_timestamp(2, " ALLOCATING IMAGE COLORS: ");
431 /* create XImage from internal image structure and convert it to Pixmap */
433 display_bits_per_pixel = bitsPerPixelAtDepth(display, screen, depth);
434 display_bytes_per_pixel = (display_bits_per_pixel + 7) / 8;
436 ximage = XCreateImage(display, visual, depth, ZPixmap, 0,
437 NULL, image->width, image->height,
438 8, image->width * display_bytes_per_pixel);
440 checked_malloc(image->width * image->height * display_bytes_per_pixel);
441 ximage->byte_order = MSBFirst;
443 src_ptr = image->data;
444 dst_ptr = (byte *)ximage->data;
446 switch (visual->class)
457 for (y=0; y<image->height; y++) /* general case */
459 for (x=0; x<image->width; x++)
463 redvalue[image->rgb.red[pixval] >> 8] |
464 greenvalue[image->rgb.green[pixval] >> 8] |
465 bluevalue[image->rgb.blue[pixval] >> 8];
466 value_to_memory(pixval, dst_ptr, display_bytes_per_pixel);
467 dst_ptr += display_bytes_per_pixel;
473 case IMAGETYPE_TRUECOLOR:
475 for (y=0; y<image->height; y++) /* general case */
477 for (x=0; x<image->width; x++)
479 pixval = memory_to_value(src_ptr, image->bytes_per_pixel);
481 redvalue[TRUECOLOR_RED(pixval)] |
482 greenvalue[TRUECOLOR_GREEN(pixval)] |
483 bluevalue[TRUECOLOR_BLUE(pixval)];
484 value_to_memory(pixval, dst_ptr, display_bytes_per_pixel);
485 src_ptr += image->bytes_per_pixel;
486 dst_ptr += display_bytes_per_pixel;
493 Error(ERR_RETURN, "RGB or TrueColor image needed");
494 SetError(error, "image type not supported");
502 if (display_bytes_per_pixel == 1) /* special case */
504 for (y=0; y<image->height; y++)
505 for (x=0; x<image->width; x++)
506 *dst_ptr++ = ximageinfo->index[c + *src_ptr++];
508 else /* general case */
510 for (y=0; y<image->height; y++)
512 for (x=0; x<image->width; x++)
514 value_to_memory(ximageinfo->index[c + *src_ptr++],
515 dst_ptr, display_bytes_per_pixel);
516 dst_ptr += display_bytes_per_pixel;
524 Error(ERR_RETURN,"DirectColor, TrueColor or PseudoColor display needed");
525 SetError(error, "display class not supported");
531 free((byte *)redvalue);
532 free((byte *)greenvalue);
533 free((byte *)bluevalue);
537 debug_print_timestamp(2, " CONVERTING IMAGE TO XIMAGE:");
540 ximageinfo->pixmap = XCreatePixmap(display, window,
541 ximage->width, ximage->height,
544 XPutImage(ximageinfo->display, ximageinfo->pixmap, gc,
545 ximage, 0, 0, 0, 0, ximage->width, ximage->height);
549 XDestroyImage(ximage);
554 void freeXImage(Image *image, XImageInfo *ximageinfo)
556 if (ximageinfo->index != NULL && ximageinfo->no > 0)
557 XFreeColors(ximageinfo->display, ximageinfo->cmap, ximageinfo->index,
559 /* this ^^^^^^^^^^^^^^ is wrong, because the used color cells
560 * are somewhere between 0 and MAX_COLORS; there are indeed 'ximageinfo->no'
561 * used color cells, but they are not at array position 0 - 'ximageinfo->no'
567 int Read_PCX_to_Pixmap(Display *display, Window window, GC gc, char *filename,
568 Pixmap *pixmap, Pixmap *pixmap_mask)
571 XImageInfo *ximageinfo;
577 debug_print_timestamp(2, NULL); /* initialize timestamp function */
580 /* read the graphic file in PCX format to image structure */
581 if ((image = Read_PCX_to_Image(filename)) == NULL)
585 printf("%s:\n", filename);
586 debug_print_timestamp(2, " READING PCX FILE TO IMAGE: ");
589 screen = DefaultScreen(display);
590 visual = DefaultVisual(display, screen);
591 depth = DefaultDepth(display, screen);
593 /* convert image structure to X11 Pixmap */
594 if (!(ximageinfo = Image_to_Pixmap(display, screen, visual,
595 window, gc, depth, image)))
596 return PCX_OtherError;
598 /* if a private colormap has been created, install it */
599 if (ximageinfo->cmap != DefaultColormap(display, screen))
600 XSetWindowColormap(display, window, ximageinfo->cmap);
603 debug_print_timestamp(2, " CONVERTING IMAGE TO PIXMAP:");
606 /* create clip mask for the image */
607 ximageinfo->pixmap_mask = Image_to_Mask(image, display, window);
610 debug_print_timestamp(2, " CONVERTING IMAGE TO MASK: ");
613 *pixmap = ximageinfo->pixmap;
614 *pixmap_mask = ximageinfo->pixmap_mask;
619 #endif /* PLATFORM_UNIX */
620 #endif /* TARGET_X11 */