1 /***********************************************************
2 * Artsoft Retro-Game Library *
3 *----------------------------------------------------------*
4 * (c) 1994-2002 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
19 /* ========================================================================= */
20 /* PLATFORM SPECIFIC IMAGE FUNCTIONS */
21 /* ========================================================================= */
23 #if defined(TARGET_X11)
25 /* for MS-DOS/Allegro, exclude all except newImage() and freeImage() */
27 Image *newImage(unsigned int width, unsigned int height, unsigned int depth)
30 unsigned int bytes_per_pixel = (depth + 7) / 8;
35 Error(ERR_EXIT, "images with more than 256 colors are not supported");
40 image = checked_calloc(sizeof(Image));
41 image->data = checked_calloc(width * height * bytes_per_pixel);
43 image->height = height;
45 image->bytes_per_pixel = bytes_per_pixel;
46 image->bytes_per_row = width * bytes_per_pixel;
49 for (i=0; i<MAX_COLORS; i++)
50 image->rgb.color_used[i] = FALSE;
52 image->type = (depth < 8 ? IMAGETYPE_BITMAP :
53 depth > 8 ? IMAGETYPE_TRUECOLOR : IMAGETYPE_RGB);
58 void freeImage(Image *image)
64 #if defined(PLATFORM_UNIX)
66 /* extra colors to try allocating in private color maps to minimize flashing */
67 #define NOFLASH_COLORS 256
69 /* architecture independent value <-> memory conversions;
70 note: the internal format is big endian */
72 #define memory_to_value(ptr, len) ( \
73 (len) == 1 ? (unsigned long)( *( (byte *)(ptr)) ) : \
74 (len) == 2 ? (unsigned long)(((unsigned long)(*( (byte *)(ptr)) ))<< 8) \
75 + ( *(((byte *)(ptr))+1) ) : \
76 (len) == 3 ? (unsigned long)(((unsigned long)(*( (byte *)(ptr)) ))<<16) \
77 + (((unsigned long)(*(((byte *)(ptr))+1)))<< 8) \
78 + ( *(((byte *)(ptr))+2) ) : \
79 (unsigned long)(((unsigned long)(*( (byte *)(ptr)) ))<<24) \
80 + (((unsigned long)(*(((byte *)(ptr))+1)))<<16) \
81 + (((unsigned long)(*(((byte *)(ptr))+2)))<< 8) \
82 + ( *(((byte *)(ptr))+3) ) )
85 #define value_to_memory(value, ptr, len) ( \
86 (len) == 1 ? (*( (byte *)(ptr) ) = ( value ) ) : \
87 (len) == 2 ? (*( (byte *)(ptr) ) = (((unsigned long)(value))>> 8), \
88 *(((byte *)(ptr))+1) = ( value ) ) : \
89 (len) == 3 ? (*( (byte *)(ptr) ) = (((unsigned long)(value))>>16), \
90 *(((byte *)(ptr))+1) = (((unsigned long)(value))>> 8), \
91 *(((byte *)(ptr))+2) = ( value ) ) : \
92 (*( (byte *)(ptr) ) = (((unsigned long)(value))>>24), \
93 *(((byte *)(ptr))+1) = (((unsigned long)(value))>>16), \
94 *(((byte *)(ptr))+2) = (((unsigned long)(value))>> 8), \
95 *(((byte *)(ptr))+3) = ( value ) ))
97 static Pixmap Image_to_Mask(Image *image, Display *display, Window window)
99 byte *src_ptr, *dst_ptr, *dst_ptr2;
100 unsigned int bytes_per_row;
101 unsigned int x, y, i;
106 bytes_per_row = (image->width + 7) / 8;
107 mask_data = checked_calloc(bytes_per_row * image->height);
109 src_ptr = image->data;
112 /* create bitmap data which can be used by 'XCreateBitmapFromData()'
113 * directly to create a pixmap of depth 1 for use as a clip mask for
114 * the corresponding image pixmap
117 for (y=0; y<image->height; y++)
119 bitmask = 0x01; /* start with leftmost bit in the byte */
120 dst_ptr2 = dst_ptr; /* start with leftmost byte in the row */
122 for (x=0; x<image->width; x++)
124 for (i=0; i<image->bytes_per_pixel; i++)
125 if (*src_ptr++) /* source pixel solid? (pixel index != 0) */
126 *dst_ptr2 |= bitmask; /* then write a bit into the image mask */
128 if ((bitmask <<= 1) == 0) /* bit at rightmost byte position reached? */
130 bitmask = 0x01; /* start again with leftmost bit position */
131 dst_ptr2++; /* continue with next byte in image mask */
135 dst_ptr += bytes_per_row; /* continue with leftmost byte of next row */
138 mask_pixmap = XCreateBitmapFromData(display, window, (char *)mask_data,
139 image->width, image->height);
145 static int bitsPerPixelAtDepth(Display *display, int screen, int depth)
147 XPixmapFormatValues *pixmap_format;
148 int i, num_pixmap_formats, bits_per_pixel = -1;
150 /* get Pixmap formats supported by the X server */
151 pixmap_format = XListPixmapFormats(display, &num_pixmap_formats);
153 /* find format that matches the given depth */
154 for (i=0; i<num_pixmap_formats; i++)
155 if (pixmap_format[i].depth == depth)
156 bits_per_pixel = pixmap_format[i].bits_per_pixel;
158 XFree(pixmap_format);
160 if (bits_per_pixel == -1)
161 Error(ERR_EXIT, "cannot find pixmap format for depth %d", depth);
163 return bits_per_pixel;
166 XImageInfo *Image_to_Pixmap(Display *display, int screen, Visual *visual,
167 Window window, GC gc, int depth, Image *image)
169 static XColor xcolor_private[NOFLASH_COLORS];
170 static int colorcell_used[NOFLASH_COLORS];
171 static Colormap global_cmap = 0;
172 static Pixel *global_cmap_index;
173 static int num_cmap_entries, free_cmap_entries;
174 static boolean private_cmap = FALSE;
175 Pixel *redvalue, *greenvalue, *bluevalue;
176 unsigned int display_bytes_per_pixel, display_bits_per_pixel;
177 unsigned int a, c = 0, x, y;
180 XImageInfo *ximageinfo;
181 byte *src_ptr, *dst_ptr;
182 char *error = "Image_to_Pixmap(): %s";
184 if (image->type == IMAGETYPE_TRUECOLOR && depth == 8)
186 SetError(error, "cannot handle true-color images on 8-bit display");
192 if (visual == DefaultVisual(display, screen))
193 global_cmap = DefaultColormap(display, screen);
196 global_cmap = XCreateColormap(display, RootWindow(display, screen),
202 xcolor.flags = DoRed | DoGreen | DoBlue;
203 redvalue = greenvalue = bluevalue = NULL;
204 ximageinfo = checked_malloc(sizeof(XImageInfo));
205 ximageinfo->display = display;
206 ximageinfo->depth = depth;
208 switch (visual->class)
214 unsigned int redcolors, greencolors, bluecolors;
215 unsigned int redstep, greenstep, bluestep;
216 unsigned int redbottom, greenbottom, bluebottom;
217 unsigned int redtop, greentop, bluetop;
219 redvalue = (Pixel *)checked_malloc(sizeof(Pixel) * 256);
220 greenvalue = (Pixel *)checked_malloc(sizeof(Pixel) * 256);
221 bluevalue = (Pixel *)checked_malloc(sizeof(Pixel) * 256);
223 ximageinfo->cmap = global_cmap;
225 retry_direct: /* tag we hit if a DirectColor allocation fails on
226 * default colormap */
228 /* calculate number of distinct colors in each band */
230 redcolors = greencolors = bluecolors = 1;
231 for (pixval=1; pixval; pixval <<= 1)
233 if (pixval & visual->red_mask)
235 if (pixval & visual->green_mask)
237 if (pixval & visual->blue_mask)
241 /* consistency check */
242 if (redcolors > visual->map_entries ||
243 greencolors > visual->map_entries ||
244 bluecolors > visual->map_entries)
245 Error(ERR_WARN, "inconsistency in color information");
247 redstep = 256 / redcolors;
248 greenstep = 256 / greencolors;
249 bluestep = 256 / bluecolors;
250 redbottom = greenbottom = bluebottom = 0;
251 redtop = greentop = bluetop = 0;
253 for (a=0; a<visual->map_entries; a++)
256 redtop = redbottom + redstep;
257 if (greenbottom < 256)
258 greentop = greenbottom + greenstep;
259 if (bluebottom < 256)
260 bluetop = bluebottom + bluestep;
262 xcolor.red = (redtop - 1) << 8;
263 xcolor.green = (greentop - 1) << 8;
264 xcolor.blue = (bluetop - 1) << 8;
265 if (!XAllocColor(display, ximageinfo->cmap, &xcolor))
267 /* if an allocation fails for a DirectColor default visual then
268 we should create a private colormap and try again. */
270 if ((visual->class == DirectColor) &&
271 (visual == DefaultVisual(display, screen)))
273 global_cmap = XCopyColormapAndFree(display, global_cmap);
274 ximageinfo->cmap = global_cmap;
280 /* something completely unexpected happened */
282 fprintf(stderr, "Image_to_Pixmap: XAllocColor failed on a TrueColor/Directcolor visual\n");
292 /* fill in pixel values for each band at this intensity */
294 while ((redbottom < 256) && (redbottom < redtop))
295 redvalue[redbottom++] = xcolor.pixel & visual->red_mask;
296 while ((greenbottom < 256) && (greenbottom < greentop))
297 greenvalue[greenbottom++] = xcolor.pixel & visual->green_mask;
298 while ((bluebottom < 256) && (bluebottom < bluetop))
299 bluevalue[bluebottom++] = xcolor.pixel & visual->blue_mask;
307 ximageinfo->cmap = global_cmap;
309 for (a=0; a<MAX_COLORS; a++)
316 if (!image->rgb.color_used[a])
319 xcolor.red = *(image->rgb.red + a);
320 xcolor.green = *(image->rgb.green + a);
321 xcolor.blue = *(image->rgb.blue + a);
323 /* look if this color already exists in our colormap */
324 if (!XAllocColor(display, ximageinfo->cmap, &xcolor))
329 Error(ERR_RETURN, "switching to private colormap");
331 /* we just filled up the default colormap -- get a private one
332 which contains all already allocated colors */
334 global_cmap = XCopyColormapAndFree(display, global_cmap);
335 ximageinfo->cmap = global_cmap;
338 /* allocate the rest of the color cells read/write */
340 (Pixel *)checked_malloc(sizeof(Pixel) * NOFLASH_COLORS);
341 for (i=0; i<NOFLASH_COLORS; i++)
342 if (!XAllocColorCells(display, global_cmap, FALSE, NULL, 0,
343 global_cmap_index + i, 1))
345 num_cmap_entries = free_cmap_entries = i;
348 printf("We've got %d free colormap entries.\n", free_cmap_entries);
351 /* to minimize colormap flashing, copy default colors and try
352 to keep them as near as possible to the old values */
354 for(i=0; i<num_cmap_entries; i++)
356 xcolor2.pixel = *(global_cmap_index + i);
357 XQueryColor(display, DefaultColormap(display, screen), &xcolor2);
358 XStoreColor(display, global_cmap, &xcolor2);
359 xcolor_private[xcolor2.pixel] = xcolor2;
360 colorcell_used[xcolor2.pixel] = FALSE;
363 /* now we have the default colormap private: all colors we
364 successfully allocated so far are read-only, which is okay,
365 because we don't want to change them anymore -- if we need
366 an existing color again, we get it by XAllocColor; all other
367 colors are read/write and we can set them by XStoreColor,
368 but we will try to overwrite those color cells with our new
369 color which are as close as possible to our new color */
372 /* look for an existing default color close the one we want */
379 for (i=num_cmap_entries-1; i>=0; i--)
381 xcolor2.pixel = *(global_cmap_index + i);
382 xcolor2 = xcolor_private[xcolor2.pixel];
384 if (colorcell_used[xcolor2.pixel])
387 if ((xcolor.red & mask) == (xcolor2.red & mask) &&
388 (xcolor.green & mask) == (xcolor2.green & mask) &&
389 (xcolor.blue & mask) == (xcolor2.blue & mask))
392 printf("replacing color cell %ld with a close color\n",
403 mask = (mask << 1) & 0xffff;
406 if (!color_found) /* no more free color cells */
408 SetError(error, "cannot allocate enough color cells");
412 xcolor.pixel = xcolor2.pixel;
413 xcolor_private[xcolor.pixel] = xcolor;
414 colorcell_used[xcolor.pixel] = TRUE;
415 XStoreColor(display, ximageinfo->cmap, &xcolor);
419 *(ximageinfo->index + a) = xcolor.pixel;
423 printf("still %d free colormap entries\n", free_cmap_entries);
426 ximageinfo->no = a; /* number of pixels allocated for this image */
430 Error(ERR_RETURN,"DirectColor, TrueColor or PseudoColor display needed");
431 SetError(error, "display class not supported");
437 debug_print_timestamp(2, " ALLOCATING IMAGE COLORS: ");
440 /* create XImage from internal image structure and convert it to Pixmap */
442 display_bits_per_pixel = bitsPerPixelAtDepth(display, screen, depth);
443 display_bytes_per_pixel = (display_bits_per_pixel + 7) / 8;
445 ximage = XCreateImage(display, visual, depth, ZPixmap, 0,
446 NULL, image->width, image->height,
447 8, image->width * display_bytes_per_pixel);
449 checked_malloc(image->width * image->height * display_bytes_per_pixel);
450 ximage->byte_order = MSBFirst;
452 src_ptr = image->data;
453 dst_ptr = (byte *)ximage->data;
455 switch (visual->class)
466 for (y=0; y<image->height; y++) /* general case */
468 for (x=0; x<image->width; x++)
472 redvalue[image->rgb.red[pixval] >> 8] |
473 greenvalue[image->rgb.green[pixval] >> 8] |
474 bluevalue[image->rgb.blue[pixval] >> 8];
475 value_to_memory(pixval, dst_ptr, display_bytes_per_pixel);
476 dst_ptr += display_bytes_per_pixel;
483 case IMAGETYPE_TRUECOLOR:
485 for (y=0; y<image->height; y++) /* general case */
487 for (x=0; x<image->width; x++)
489 pixval = memory_to_value(src_ptr, image->bytes_per_pixel);
491 redvalue[TRUECOLOR_RED(pixval)] |
492 greenvalue[TRUECOLOR_GREEN(pixval)] |
493 bluevalue[TRUECOLOR_BLUE(pixval)];
494 value_to_memory(pixval, dst_ptr, display_bytes_per_pixel);
495 src_ptr += image->bytes_per_pixel;
496 dst_ptr += display_bytes_per_pixel;
504 Error(ERR_RETURN, "RGB or TrueColor image needed");
505 SetError(error, "image type not supported");
515 if (display_bytes_per_pixel == 1) /* special case */
517 for (y=0; y<image->height; y++)
518 for (x=0; x<image->width; x++)
519 *dst_ptr++ = ximageinfo->index[c + *src_ptr++];
521 else /* general case */
523 for (y=0; y<image->height; y++)
525 for (x=0; x<image->width; x++)
527 value_to_memory(ximageinfo->index[c + *src_ptr++],
528 dst_ptr, display_bytes_per_pixel);
529 dst_ptr += display_bytes_per_pixel;
538 Error(ERR_RETURN,"DirectColor, TrueColor or PseudoColor display needed");
539 SetError(error, "display class not supported");
546 free((byte *)redvalue);
547 free((byte *)greenvalue);
548 free((byte *)bluevalue);
552 debug_print_timestamp(2, " CONVERTING IMAGE TO XIMAGE:");
555 ximageinfo->pixmap = XCreatePixmap(display, window,
556 ximage->width, ximage->height,
559 XPutImage(ximageinfo->display, ximageinfo->pixmap, gc,
560 ximage, 0, 0, 0, 0, ximage->width, ximage->height);
562 XDestroyImage(ximage);
567 void freeXImage(Image *image, XImageInfo *ximageinfo)
569 if (ximageinfo->index != NULL && ximageinfo->no > 0)
570 XFreeColors(ximageinfo->display, ximageinfo->cmap, ximageinfo->index,
572 /* this ^^^^^^^^^^^^^^ is wrong, because the used color cells
573 * are somewhere between 0 and MAX_COLORS; there are indeed 'ximageinfo->no'
574 * used color cells, but they are not at array position 0 - 'ximageinfo->no'
580 int Read_PCX_to_Pixmap(Display *display, Window window, GC gc, char *filename,
581 Pixmap *pixmap, Pixmap *pixmap_mask)
584 XImageInfo *ximageinfo;
590 debug_print_timestamp(2, NULL); /* initialize timestamp function */
593 /* read the graphic file in PCX format to image structure */
594 if ((image = Read_PCX_to_Image(filename)) == NULL)
598 printf("%s:\n", filename);
599 debug_print_timestamp(2, " READING PCX FILE TO IMAGE: ");
602 screen = DefaultScreen(display);
603 visual = DefaultVisual(display, screen);
604 depth = DefaultDepth(display, screen);
606 /* convert image structure to X11 Pixmap */
607 if (!(ximageinfo = Image_to_Pixmap(display, screen, visual,
608 window, gc, depth, image)))
612 return PCX_OtherError;
615 /* if a private colormap has been created, install it */
616 if (ximageinfo->cmap != DefaultColormap(display, screen))
617 XSetWindowColormap(display, window, ximageinfo->cmap);
620 debug_print_timestamp(2, " CONVERTING IMAGE TO PIXMAP:");
623 /* create clip mask for the image */
624 ximageinfo->pixmap_mask = Image_to_Mask(image, display, window);
627 debug_print_timestamp(2, " CONVERTING IMAGE TO MASK: ");
630 *pixmap = ximageinfo->pixmap;
631 *pixmap_mask = ximageinfo->pixmap_mask;
633 /* free generic image and ximageinfo after native Pixmap has been created */
640 #endif /* PLATFORM_UNIX */
641 #endif /* TARGET_X11 */
644 /* ========================================================================= */
645 /* PLATFORM INDEPENDANT IMAGE FUNCTIONS */
646 /* ========================================================================= */
650 char *source_filename;
655 typedef struct ImageInfo ImageInfo;
657 static struct ArtworkListInfo *image_info = NULL;
659 static void *Load_PCX(char *filename)
664 printf("loading PCX file '%s'\n", filename);
667 img_info = checked_calloc(sizeof(ImageInfo));
669 if ((img_info->bitmap = LoadImage(filename)) == NULL)
671 Error(ERR_WARN, "cannot read image file '%s': LoadImage() failed: %s",
672 filename, GetError());
677 img_info->source_filename = getStringCopy(filename);
682 static void FreeImage(void *ptr)
684 ImageInfo *image = (ImageInfo *)ptr;
690 FreeBitmap(image->bitmap);
692 if (image->source_filename)
693 free(image->source_filename);
698 struct FileInfo *getCurrentImageList()
700 return image_info->file_list;
703 Bitmap *getBitmapFromImageID(int graphic)
705 ImageInfo **img_info = (ImageInfo **)image_info->artwork_list;
707 return img_info[graphic]->bitmap;
710 void InitImageList(struct ConfigInfo *config_list,
711 struct ConfigInfo *config_suffix_list,
712 int num_file_list_entries)
716 image_info = checked_calloc(sizeof(struct ArtworkListInfo));
718 image_info->type = ARTWORK_TYPE_GRAPHICS;
720 image_info->num_file_list_entries = num_file_list_entries;
721 image_info->num_suffix_list_entries = 0;
722 for (i=0; config_suffix_list[i].token != NULL; i++)
723 image_info->num_suffix_list_entries++;
725 image_info->file_list =
726 getFileListFromConfigList(config_list, config_suffix_list,
727 num_file_list_entries);
728 image_info->suffix_list = config_suffix_list;
730 image_info->artwork_list =
731 checked_calloc(num_file_list_entries * sizeof(ImageInfo *));
733 image_info->content_list = NULL;
735 image_info->load_artwork = Load_PCX;
736 image_info->free_artwork = FreeImage;
739 void ReloadCustomImages()
742 printf("DEBUG: reloading images '%s' ...\n", artwork.gfx_current_identifier);
745 ReloadCustomArtworkList(image_info);
750 FreeCustomArtworkList(image_info);