1 /***********************************************************
2 * Artsoft Retro-Game Library *
3 *----------------------------------------------------------*
4 * (c) 1994-2000 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");
33 image = checked_malloc(sizeof(Image));
34 image->data = checked_malloc(width * height * bytes_per_pixel);
36 image->height = height;
39 for (i=0; i<MAX_COLORS; i++)
40 image->rgb.color_used[i] = FALSE;
45 void freeImage(Image *image)
51 #if defined(PLATFORM_UNIX)
53 /* extra colors to try allocating in private color maps to minimize flashing */
54 #define NOFLASH_COLORS 256
56 /* architecture independent value-to-memory conversion
57 note: the internal format is big endian */
59 #define value_to_memory(value, ptr, length) ( \
60 (length) == 1 ? (*( (byte *)(ptr) ) = ( value ) ) : \
61 (length) == 2 ? (*( (byte *)(ptr) ) = (((unsigned long)(value))>> 8), \
62 *(((byte *)(ptr))+1) = ( value ) ) : \
63 (length) == 3 ? (*( (byte *)(ptr) ) = (((unsigned long)(value))>>16), \
64 *(((byte *)(ptr))+1) = (((unsigned long)(value))>> 8), \
65 *(((byte *)(ptr))+2) = ( value ) ) : \
66 (*( (byte *)(ptr) ) = (((unsigned long)(value))>>24), \
67 *(((byte *)(ptr))+1) = (((unsigned long)(value))>>16), \
68 *(((byte *)(ptr))+2) = (((unsigned long)(value))>> 8), \
69 *(((byte *)(ptr))+3) = ( value ) ))
71 static Pixmap Image_to_Mask(Image *image, Display *display, Window window)
73 byte *src_ptr, *dst_ptr, *dst_ptr2;
74 unsigned int bytes_per_row;
80 bytes_per_row = (image->width + 7) / 8;
81 mask_data = checked_calloc(bytes_per_row * image->height);
83 src_ptr = image->data;
86 /* create bitmap data which can be used by 'XCreateBitmapFromData()'
87 * directly to create a pixmap of depth 1 for use as a clip mask for
88 * the corresponding image pixmap
91 for (y=0; y<image->height; y++)
93 bitmask = 0x01; /* start with leftmost bit in the byte */
94 dst_ptr2 = dst_ptr; /* start with leftmost byte in the row */
96 for (x=0; x<image->width; x++)
98 if (*src_ptr++) /* source pixel solid? (pixel index != 0) */
99 *dst_ptr2 |= bitmask; /* then write a bit into the image mask */
101 if ((bitmask <<= 1) == 0) /* bit at rightmost byte position reached? */
103 bitmask = 0x01; /* start again with leftmost bit position */
104 dst_ptr2++; /* continue with next byte in image mask */
108 dst_ptr += bytes_per_row; /* continue with leftmost byte of next row */
111 mask_pixmap = XCreateBitmapFromData(display, window, (char *)mask_data,
112 image->width, image->height);
118 static int bitsPerPixelAtDepth(Display *display, int screen, int depth)
120 XPixmapFormatValues *pixmap_format;
121 int i, num_pixmap_formats, bits_per_pixel = -1;
123 /* get Pixmap formats supported by the X server */
124 pixmap_format = XListPixmapFormats(display, &num_pixmap_formats);
126 /* find format that matches the given depth */
127 for (i=0; i<num_pixmap_formats; i++)
128 if (pixmap_format[i].depth == depth)
129 bits_per_pixel = pixmap_format[i].bits_per_pixel;
131 XFree(pixmap_format);
133 if (bits_per_pixel == -1)
134 Error(ERR_EXIT, "cannot find pixmap format for depth %d", depth);
136 return bits_per_pixel;
139 XImageInfo *Image_to_Pixmap(Display *display, int screen, Visual *visual,
140 Window window, GC gc, int depth, Image *image)
142 static XColor xcolor_private[NOFLASH_COLORS];
143 static int colorcell_used[NOFLASH_COLORS];
144 static Colormap global_cmap = 0;
145 static Pixel *global_cmap_index;
146 static int num_cmap_entries, free_cmap_entries;
147 static boolean private_cmap = FALSE;
148 Pixel *redvalue, *greenvalue, *bluevalue;
149 unsigned int a, c = 0, x, y, bytes_per_pixel, bits_per_pixel;
152 XImageInfo *ximageinfo;
153 byte *src_ptr, *dst_ptr;
157 if (visual == DefaultVisual(display, screen))
158 global_cmap = DefaultColormap(display, screen);
161 global_cmap = XCreateColormap(display, RootWindow(display, screen),
167 xcolor.flags = DoRed | DoGreen | DoBlue;
168 redvalue = greenvalue = bluevalue = NULL;
169 ximageinfo = checked_malloc(sizeof(XImageInfo));
170 ximageinfo->display = display;
171 ximageinfo->depth = depth;
173 switch (visual->class)
179 unsigned int redcolors, greencolors, bluecolors;
180 unsigned int redstep, greenstep, bluestep;
181 unsigned int redbottom, greenbottom, bluebottom;
182 unsigned int redtop, greentop, bluetop;
184 redvalue = (Pixel *)checked_malloc(sizeof(Pixel) * 256);
185 greenvalue = (Pixel *)checked_malloc(sizeof(Pixel) * 256);
186 bluevalue = (Pixel *)checked_malloc(sizeof(Pixel) * 256);
188 ximageinfo->cmap = global_cmap;
190 retry_direct: /* tag we hit if a DirectColor allocation fails on
191 * default colormap */
193 /* calculate number of distinct colors in each band */
195 redcolors = greencolors = bluecolors = 1;
196 for (pixval=1; pixval; pixval <<= 1)
198 if (pixval & visual->red_mask)
200 if (pixval & visual->green_mask)
202 if (pixval & visual->blue_mask)
206 /* consistency check */
207 if (redcolors > visual->map_entries ||
208 greencolors > visual->map_entries ||
209 bluecolors > visual->map_entries)
210 Error(ERR_WARN, "inconsistency in color information");
212 redstep = 256 / redcolors;
213 greenstep = 256 / greencolors;
214 bluestep = 256 / bluecolors;
215 redbottom = greenbottom = bluebottom = 0;
216 redtop = greentop = bluetop = 0;
217 for (a=0; a<visual->map_entries; a++)
220 redtop = redbottom + redstep;
221 if (greenbottom < 256)
222 greentop = greenbottom + greenstep;
223 if (bluebottom < 256)
224 bluetop = bluebottom + bluestep;
226 xcolor.red = (redtop - 1) << 8;
227 xcolor.green = (greentop - 1) << 8;
228 xcolor.blue = (bluetop - 1) << 8;
229 if (!XAllocColor(display, ximageinfo->cmap, &xcolor))
231 /* if an allocation fails for a DirectColor default visual then
232 we should create a private colormap and try again. */
234 if ((visual->class == DirectColor) &&
235 (visual == DefaultVisual(display, screen)))
237 global_cmap = XCopyColormapAndFree(display, global_cmap);
238 ximageinfo->cmap = global_cmap;
244 /* something completely unexpected happened */
246 fprintf(stderr, "imageToXImage: XAllocColor failed on a TrueColor/Directcolor visual\n");
254 /* fill in pixel values for each band at this intensity */
256 while ((redbottom < 256) && (redbottom < redtop))
257 redvalue[redbottom++] = xcolor.pixel & visual->red_mask;
258 while ((greenbottom < 256) && (greenbottom < greentop))
259 greenvalue[greenbottom++] = xcolor.pixel & visual->green_mask;
260 while ((bluebottom < 256) && (bluebottom < bluetop))
261 bluevalue[bluebottom++] = xcolor.pixel & visual->blue_mask;
268 ximageinfo->cmap = global_cmap;
270 for (a=0; a<MAX_COLORS; a++)
277 if (!image->rgb.color_used[a])
280 xcolor.red = *(image->rgb.red + a);
281 xcolor.green = *(image->rgb.green + a);
282 xcolor.blue = *(image->rgb.blue + a);
284 /* look if this color already exists in our colormap */
285 if (!XAllocColor(display, ximageinfo->cmap, &xcolor))
290 Error(ERR_RETURN, "switching to private colormap");
292 /* we just filled up the default colormap -- get a private one
293 which contains all already allocated colors */
295 global_cmap = XCopyColormapAndFree(display, global_cmap);
296 ximageinfo->cmap = global_cmap;
299 /* allocate the rest of the color cells read/write */
301 (Pixel *)checked_malloc(sizeof(Pixel) * NOFLASH_COLORS);
302 for (i=0; i<NOFLASH_COLORS; i++)
303 if (!XAllocColorCells(display, global_cmap, FALSE, NULL, 0,
304 global_cmap_index + i, 1))
306 num_cmap_entries = free_cmap_entries = i;
309 printf("We've got %d free colormap entries.\n", free_cmap_entries);
312 /* to minimize colormap flashing, copy default colors and try
313 to keep them as near as possible to the old values */
315 for(i=0; i<num_cmap_entries; i++)
317 xcolor2.pixel = *(global_cmap_index + i);
318 XQueryColor(display, DefaultColormap(display, screen), &xcolor2);
319 XStoreColor(display, global_cmap, &xcolor2);
320 xcolor_private[xcolor2.pixel] = xcolor2;
321 colorcell_used[xcolor2.pixel] = FALSE;
324 /* now we have the default colormap private: all colors we
325 successfully allocated so far are read-only, which is okay,
326 because we don't want to change them anymore -- if we need
327 an existing color again, we get it by XAllocColor; all other
328 colors are read/write and we can set them by XStoreColor,
329 but we will try to overwrite those color cells with our new
330 color which are as close as possible to our new color */
333 /* look for an existing default color close the one we want */
340 for (i=num_cmap_entries-1; i>=0; i--)
342 xcolor2.pixel = *(global_cmap_index + i);
343 xcolor2 = xcolor_private[xcolor2.pixel];
345 if (colorcell_used[xcolor2.pixel])
348 if ((xcolor.red & mask) == (xcolor2.red & mask) &&
349 (xcolor.green & mask) == (xcolor2.green & mask) &&
350 (xcolor.blue & mask) == (xcolor2.blue & mask))
353 printf("replacing color cell %ld with a close color\n",
364 mask = (mask << 1) & 0xffff;
367 if (!color_found) /* no more free color cells */
368 Error(ERR_EXIT, "cannot allocate enough color cells");
370 xcolor.pixel = xcolor2.pixel;
371 xcolor_private[xcolor.pixel] = xcolor;
372 colorcell_used[xcolor.pixel] = TRUE;
373 XStoreColor(display, ximageinfo->cmap, &xcolor);
377 *(ximageinfo->index + a) = xcolor.pixel;
381 printf("still %d free colormap entries\n", free_cmap_entries);
384 ximageinfo->no = a; /* number of pixels allocated for this image */
388 Error(ERR_RETURN, "display class not supported");
389 Error(ERR_EXIT, "DirectColor, TrueColor or PseudoColor display needed");
394 debug_print_timestamp(2, " ALLOCATING IMAGE COLORS: ");
397 /* create XImage from internal image structure and convert it to Pixmap */
399 bits_per_pixel = bitsPerPixelAtDepth(display, screen, depth);
400 bytes_per_pixel = (bits_per_pixel + 7) / 8;
402 ximage = XCreateImage(display, visual, depth, ZPixmap, 0,
403 NULL, image->width, image->height,
404 8, image->width * bytes_per_pixel);
406 checked_malloc(image->width * image->height * bytes_per_pixel);
407 ximage->byte_order = MSBFirst;
409 src_ptr = image->data;
410 dst_ptr = (byte *)ximage->data;
412 switch (visual->class)
419 for (y=0; y<image->height; y++) /* general case */
421 for (x=0; x<image->width; x++)
425 redvalue[image->rgb.red[pixval] >> 8] |
426 greenvalue[image->rgb.green[pixval] >> 8] |
427 bluevalue[image->rgb.blue[pixval] >> 8];
428 value_to_memory(pixval, dst_ptr, bytes_per_pixel);
429 dst_ptr += bytes_per_pixel;
437 if (bytes_per_pixel == 1) /* (common) special case */
439 for (y=0; y<image->height; y++)
440 for (x=0; x<image->width; x++)
441 *dst_ptr++ = ximageinfo->index[c + *src_ptr++];
443 else /* general case */
445 for (y=0; y<image->height; y++)
447 for (x=0; x<image->width; x++)
449 value_to_memory(ximageinfo->index[c + *src_ptr++],
450 dst_ptr, bytes_per_pixel);
451 dst_ptr += bytes_per_pixel;
459 Error(ERR_RETURN, "display class not supported");
460 Error(ERR_EXIT, "DirectColor, TrueColor or PseudoColor display needed");
466 free((byte *)redvalue);
467 free((byte *)greenvalue);
468 free((byte *)bluevalue);
472 debug_print_timestamp(2, " CONVERTING IMAGE TO XIMAGE:");
475 ximageinfo->pixmap = XCreatePixmap(display, window,
476 ximage->width, ximage->height,
479 XPutImage(ximageinfo->display, ximageinfo->pixmap, gc,
480 ximage, 0, 0, 0, 0, ximage->width, ximage->height);
484 XDestroyImage(ximage);
489 void freeXImage(Image *image, XImageInfo *ximageinfo)
491 if (ximageinfo->index != NULL && ximageinfo->no > 0)
492 XFreeColors(ximageinfo->display, ximageinfo->cmap, ximageinfo->index,
494 /* this ^^^^^^^^^^^^^^ is wrong, because the used color cells
495 * are somewhere between 0 and MAX_COLORS; there are indeed 'ximageinfo->no'
496 * used color cells, but they are not at array position 0 - 'ximageinfo->no'
502 int Read_PCX_to_Pixmap(Display *display, Window window, GC gc, char *filename,
503 Pixmap *pixmap, Pixmap *pixmap_mask)
506 XImageInfo *ximageinfo;
512 debug_print_timestamp(2, NULL); /* initialize timestamp function */
515 /* read the graphic file in PCX format to image structure */
516 if ((image = Read_PCX_to_Image(filename)) == NULL)
520 printf("%s:\n", filename);
521 debug_print_timestamp(2, " READING PCX FILE TO IMAGE: ");
524 screen = DefaultScreen(display);
525 visual = DefaultVisual(display, screen);
526 depth = DefaultDepth(display, screen);
528 /* convert image structure to X11 Pixmap */
529 if (!(ximageinfo = Image_to_Pixmap(display, screen, visual,
530 window, gc, depth, image)))
531 Error(ERR_EXIT, "cannot convert Image to Pixmap");
533 /* if a private colormap has been created, install it */
534 if (ximageinfo->cmap != DefaultColormap(display, screen))
535 XSetWindowColormap(display, window, ximageinfo->cmap);
538 debug_print_timestamp(2, " CONVERTING IMAGE TO PIXMAP:");
541 /* create clip mask for the image */
542 ximageinfo->pixmap_mask = Image_to_Mask(image, display, window);
545 debug_print_timestamp(2, " CONVERTING IMAGE TO MASK: ");
548 *pixmap = ximageinfo->pixmap;
549 *pixmap_mask = ximageinfo->pixmap_mask;
554 #endif /* PLATFORM_UNIX */
555 #endif /* TARGET_X11 */