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 ***********************************************************/
20 /* ========================================================================= */
21 /* PLATFORM SPECIFIC IMAGE FUNCTIONS */
22 /* ========================================================================= */
24 #if defined(TARGET_X11)
26 /* for MS-DOS/Allegro, exclude all except newImage() and freeImage() */
28 Image *newImage(unsigned int width, unsigned int height, unsigned int depth)
31 unsigned int bytes_per_pixel = (depth + 7) / 8;
36 Error(ERR_EXIT, "images with more than 256 colors are not supported");
41 image = checked_calloc(sizeof(Image));
42 image->data = checked_calloc(width * height * bytes_per_pixel);
44 image->height = height;
46 image->bytes_per_pixel = bytes_per_pixel;
47 image->bytes_per_row = width * bytes_per_pixel;
50 for (i=0; i<MAX_COLORS; i++)
51 image->rgb.color_used[i] = FALSE;
53 image->type = (depth < 8 ? IMAGETYPE_BITMAP :
54 depth > 8 ? IMAGETYPE_TRUECOLOR : IMAGETYPE_RGB);
59 void freeImage(Image *image)
65 #if defined(PLATFORM_UNIX)
67 /* extra colors to try allocating in private color maps to minimize flashing */
68 #define NOFLASH_COLORS 256
70 /* architecture independent value <-> memory conversions;
71 note: the internal format is big endian */
73 #define memory_to_value(ptr, len) ( \
74 (len) == 1 ? (unsigned long)( *( (byte *)(ptr)) ) : \
75 (len) == 2 ? (unsigned long)(((unsigned long)(*( (byte *)(ptr)) ))<< 8) \
76 + ( *(((byte *)(ptr))+1) ) : \
77 (len) == 3 ? (unsigned long)(((unsigned long)(*( (byte *)(ptr)) ))<<16) \
78 + (((unsigned long)(*(((byte *)(ptr))+1)))<< 8) \
79 + ( *(((byte *)(ptr))+2) ) : \
80 (unsigned long)(((unsigned long)(*( (byte *)(ptr)) ))<<24) \
81 + (((unsigned long)(*(((byte *)(ptr))+1)))<<16) \
82 + (((unsigned long)(*(((byte *)(ptr))+2)))<< 8) \
83 + ( *(((byte *)(ptr))+3) ) )
86 #define value_to_memory(value, ptr, len) ( \
87 (len) == 1 ? (*( (byte *)(ptr) ) = ( value ) ) : \
88 (len) == 2 ? (*( (byte *)(ptr) ) = (((unsigned long)(value))>> 8), \
89 *(((byte *)(ptr))+1) = ( value ) ) : \
90 (len) == 3 ? (*( (byte *)(ptr) ) = (((unsigned long)(value))>>16), \
91 *(((byte *)(ptr))+1) = (((unsigned long)(value))>> 8), \
92 *(((byte *)(ptr))+2) = ( value ) ) : \
93 (*( (byte *)(ptr) ) = (((unsigned long)(value))>>24), \
94 *(((byte *)(ptr))+1) = (((unsigned long)(value))>>16), \
95 *(((byte *)(ptr))+2) = (((unsigned long)(value))>> 8), \
96 *(((byte *)(ptr))+3) = ( value ) ))
98 static Pixmap Image_to_Mask(Image *image, Display *display, Window window)
100 byte *src_ptr, *dst_ptr, *dst_ptr2;
101 unsigned int bytes_per_row;
102 unsigned int x, y, i;
107 bytes_per_row = (image->width + 7) / 8;
108 mask_data = checked_calloc(bytes_per_row * image->height);
110 src_ptr = image->data;
113 /* create bitmap data which can be used by 'XCreateBitmapFromData()'
114 * directly to create a pixmap of depth 1 for use as a clip mask for
115 * the corresponding image pixmap
118 for (y=0; y<image->height; y++)
120 bitmask = 0x01; /* start with leftmost bit in the byte */
121 dst_ptr2 = dst_ptr; /* start with leftmost byte in the row */
123 for (x=0; x<image->width; x++)
125 for (i=0; i<image->bytes_per_pixel; i++)
126 if (*src_ptr++) /* source pixel solid? (pixel index != 0) */
127 *dst_ptr2 |= bitmask; /* then write a bit into the image mask */
129 if ((bitmask <<= 1) == 0) /* bit at rightmost byte position reached? */
131 bitmask = 0x01; /* start again with leftmost bit position */
132 dst_ptr2++; /* continue with next byte in image mask */
136 dst_ptr += bytes_per_row; /* continue with leftmost byte of next row */
139 mask_pixmap = XCreateBitmapFromData(display, window, (char *)mask_data,
140 image->width, image->height);
146 static int bitsPerPixelAtDepth(Display *display, int screen, int depth)
148 XPixmapFormatValues *pixmap_format;
149 int i, num_pixmap_formats, bits_per_pixel = -1;
151 /* get Pixmap formats supported by the X server */
152 pixmap_format = XListPixmapFormats(display, &num_pixmap_formats);
154 /* find format that matches the given depth */
155 for (i=0; i<num_pixmap_formats; i++)
156 if (pixmap_format[i].depth == depth)
157 bits_per_pixel = pixmap_format[i].bits_per_pixel;
159 XFree(pixmap_format);
161 if (bits_per_pixel == -1)
162 Error(ERR_EXIT, "cannot find pixmap format for depth %d", depth);
164 return bits_per_pixel;
167 XImageInfo *Image_to_Pixmap(Display *display, int screen, Visual *visual,
168 Window window, GC gc, int depth, Image *image)
170 static XColor xcolor_private[NOFLASH_COLORS];
171 static int colorcell_used[NOFLASH_COLORS];
172 static Colormap global_cmap = 0;
173 static Pixel *global_cmap_index;
174 static int num_cmap_entries, free_cmap_entries;
175 static boolean private_cmap = FALSE;
176 Pixel *redvalue, *greenvalue, *bluevalue;
177 unsigned int display_bytes_per_pixel, display_bits_per_pixel;
178 unsigned int a, c = 0, x, y;
181 XImageInfo *ximageinfo;
182 byte *src_ptr, *dst_ptr;
183 char *error = "Image_to_Pixmap(): %s";
185 if (image->type == IMAGETYPE_TRUECOLOR && depth == 8)
187 SetError(error, "cannot handle true-color images on 8-bit display");
193 if (visual == DefaultVisual(display, screen))
194 global_cmap = DefaultColormap(display, screen);
197 global_cmap = XCreateColormap(display, RootWindow(display, screen),
203 xcolor.flags = DoRed | DoGreen | DoBlue;
204 redvalue = greenvalue = bluevalue = NULL;
205 ximageinfo = checked_malloc(sizeof(XImageInfo));
206 ximageinfo->display = display;
207 ximageinfo->depth = depth;
209 switch (visual->class)
215 unsigned int redcolors, greencolors, bluecolors;
216 unsigned int redstep, greenstep, bluestep;
217 unsigned int redbottom, greenbottom, bluebottom;
218 unsigned int redtop, greentop, bluetop;
220 redvalue = (Pixel *)checked_malloc(sizeof(Pixel) * 256);
221 greenvalue = (Pixel *)checked_malloc(sizeof(Pixel) * 256);
222 bluevalue = (Pixel *)checked_malloc(sizeof(Pixel) * 256);
224 ximageinfo->cmap = global_cmap;
226 retry_direct: /* tag we hit if a DirectColor allocation fails on
227 * default colormap */
229 /* calculate number of distinct colors in each band */
231 redcolors = greencolors = bluecolors = 1;
232 for (pixval=1; pixval; pixval <<= 1)
234 if (pixval & visual->red_mask)
236 if (pixval & visual->green_mask)
238 if (pixval & visual->blue_mask)
242 /* consistency check */
243 if (redcolors > visual->map_entries ||
244 greencolors > visual->map_entries ||
245 bluecolors > visual->map_entries)
246 Error(ERR_WARN, "inconsistency in color information");
248 redstep = 256 / redcolors;
249 greenstep = 256 / greencolors;
250 bluestep = 256 / bluecolors;
251 redbottom = greenbottom = bluebottom = 0;
252 redtop = greentop = bluetop = 0;
254 for (a=0; a<visual->map_entries; a++)
257 redtop = redbottom + redstep;
258 if (greenbottom < 256)
259 greentop = greenbottom + greenstep;
260 if (bluebottom < 256)
261 bluetop = bluebottom + bluestep;
263 xcolor.red = (redtop - 1) << 8;
264 xcolor.green = (greentop - 1) << 8;
265 xcolor.blue = (bluetop - 1) << 8;
266 if (!XAllocColor(display, ximageinfo->cmap, &xcolor))
268 /* if an allocation fails for a DirectColor default visual then
269 we should create a private colormap and try again. */
271 if ((visual->class == DirectColor) &&
272 (visual == DefaultVisual(display, screen)))
274 global_cmap = XCopyColormapAndFree(display, global_cmap);
275 ximageinfo->cmap = global_cmap;
281 /* something completely unexpected happened */
283 fprintf(stderr, "Image_to_Pixmap: XAllocColor failed on a TrueColor/Directcolor visual\n");
293 /* fill in pixel values for each band at this intensity */
295 while ((redbottom < 256) && (redbottom < redtop))
296 redvalue[redbottom++] = xcolor.pixel & visual->red_mask;
297 while ((greenbottom < 256) && (greenbottom < greentop))
298 greenvalue[greenbottom++] = xcolor.pixel & visual->green_mask;
299 while ((bluebottom < 256) && (bluebottom < bluetop))
300 bluevalue[bluebottom++] = xcolor.pixel & visual->blue_mask;
308 ximageinfo->cmap = global_cmap;
310 for (a=0; a<MAX_COLORS; a++)
317 if (!image->rgb.color_used[a])
320 xcolor.red = *(image->rgb.red + a);
321 xcolor.green = *(image->rgb.green + a);
322 xcolor.blue = *(image->rgb.blue + a);
324 /* look if this color already exists in our colormap */
325 if (!XAllocColor(display, ximageinfo->cmap, &xcolor))
330 Error(ERR_RETURN, "switching to private colormap");
332 /* we just filled up the default colormap -- get a private one
333 which contains all already allocated colors */
335 global_cmap = XCopyColormapAndFree(display, global_cmap);
336 ximageinfo->cmap = global_cmap;
339 /* allocate the rest of the color cells read/write */
341 (Pixel *)checked_malloc(sizeof(Pixel) * NOFLASH_COLORS);
342 for (i=0; i<NOFLASH_COLORS; i++)
343 if (!XAllocColorCells(display, global_cmap, FALSE, NULL, 0,
344 global_cmap_index + i, 1))
346 num_cmap_entries = free_cmap_entries = i;
349 printf("We've got %d free colormap entries.\n", free_cmap_entries);
352 /* to minimize colormap flashing, copy default colors and try
353 to keep them as near as possible to the old values */
355 for(i=0; i<num_cmap_entries; i++)
357 xcolor2.pixel = *(global_cmap_index + i);
358 XQueryColor(display, DefaultColormap(display, screen), &xcolor2);
359 XStoreColor(display, global_cmap, &xcolor2);
360 xcolor_private[xcolor2.pixel] = xcolor2;
361 colorcell_used[xcolor2.pixel] = FALSE;
364 /* now we have the default colormap private: all colors we
365 successfully allocated so far are read-only, which is okay,
366 because we don't want to change them anymore -- if we need
367 an existing color again, we get it by XAllocColor; all other
368 colors are read/write and we can set them by XStoreColor,
369 but we will try to overwrite those color cells with our new
370 color which are as close as possible to our new color */
373 /* look for an existing default color close the one we want */
380 for (i=num_cmap_entries-1; i>=0; i--)
382 xcolor2.pixel = *(global_cmap_index + i);
383 xcolor2 = xcolor_private[xcolor2.pixel];
385 if (colorcell_used[xcolor2.pixel])
388 if ((xcolor.red & mask) == (xcolor2.red & mask) &&
389 (xcolor.green & mask) == (xcolor2.green & mask) &&
390 (xcolor.blue & mask) == (xcolor2.blue & mask))
393 printf("replacing color cell %ld with a close color\n",
404 mask = (mask << 1) & 0xffff;
407 if (!color_found) /* no more free color cells */
409 SetError(error, "cannot allocate enough color cells");
413 xcolor.pixel = xcolor2.pixel;
414 xcolor_private[xcolor.pixel] = xcolor;
415 colorcell_used[xcolor.pixel] = TRUE;
416 XStoreColor(display, ximageinfo->cmap, &xcolor);
420 *(ximageinfo->index + a) = xcolor.pixel;
424 printf("still %d free colormap entries\n", free_cmap_entries);
427 ximageinfo->no = a; /* number of pixels allocated for this image */
431 Error(ERR_RETURN,"DirectColor, TrueColor or PseudoColor display needed");
432 SetError(error, "display class not supported");
438 debug_print_timestamp(2, " ALLOCATING IMAGE COLORS: ");
441 /* create XImage from internal image structure and convert it to Pixmap */
443 display_bits_per_pixel = bitsPerPixelAtDepth(display, screen, depth);
444 display_bytes_per_pixel = (display_bits_per_pixel + 7) / 8;
446 ximage = XCreateImage(display, visual, depth, ZPixmap, 0,
447 NULL, image->width, image->height,
448 8, image->width * display_bytes_per_pixel);
450 checked_malloc(image->width * image->height * display_bytes_per_pixel);
451 ximage->byte_order = MSBFirst;
453 src_ptr = image->data;
454 dst_ptr = (byte *)ximage->data;
456 switch (visual->class)
467 for (y=0; y<image->height; y++) /* general case */
469 for (x=0; x<image->width; x++)
473 redvalue[image->rgb.red[pixval] >> 8] |
474 greenvalue[image->rgb.green[pixval] >> 8] |
475 bluevalue[image->rgb.blue[pixval] >> 8];
476 value_to_memory(pixval, dst_ptr, display_bytes_per_pixel);
477 dst_ptr += display_bytes_per_pixel;
484 case IMAGETYPE_TRUECOLOR:
486 for (y=0; y<image->height; y++) /* general case */
488 for (x=0; x<image->width; x++)
490 pixval = memory_to_value(src_ptr, image->bytes_per_pixel);
492 redvalue[TRUECOLOR_RED(pixval)] |
493 greenvalue[TRUECOLOR_GREEN(pixval)] |
494 bluevalue[TRUECOLOR_BLUE(pixval)];
495 value_to_memory(pixval, dst_ptr, display_bytes_per_pixel);
496 src_ptr += image->bytes_per_pixel;
497 dst_ptr += display_bytes_per_pixel;
505 Error(ERR_RETURN, "RGB or TrueColor image needed");
506 SetError(error, "image type not supported");
516 if (display_bytes_per_pixel == 1) /* special case */
518 for (y=0; y<image->height; y++)
519 for (x=0; x<image->width; x++)
520 *dst_ptr++ = ximageinfo->index[c + *src_ptr++];
522 else /* general case */
524 for (y=0; y<image->height; y++)
526 for (x=0; x<image->width; x++)
528 value_to_memory(ximageinfo->index[c + *src_ptr++],
529 dst_ptr, display_bytes_per_pixel);
530 dst_ptr += display_bytes_per_pixel;
539 Error(ERR_RETURN,"DirectColor, TrueColor or PseudoColor display needed");
540 SetError(error, "display class not supported");
547 free((byte *)redvalue);
548 free((byte *)greenvalue);
549 free((byte *)bluevalue);
553 debug_print_timestamp(2, " CONVERTING IMAGE TO XIMAGE:");
556 ximageinfo->pixmap = XCreatePixmap(display, window,
557 ximage->width, ximage->height,
560 XPutImage(ximageinfo->display, ximageinfo->pixmap, gc,
561 ximage, 0, 0, 0, 0, ximage->width, ximage->height);
563 XDestroyImage(ximage);
568 void freeXImage(Image *image, XImageInfo *ximageinfo)
570 if (ximageinfo->index != NULL && ximageinfo->no > 0)
571 XFreeColors(ximageinfo->display, ximageinfo->cmap, ximageinfo->index,
573 /* this ^^^^^^^^^^^^^^ is wrong, because the used color cells
574 * are somewhere between 0 and MAX_COLORS; there are indeed 'ximageinfo->no'
575 * used color cells, but they are not at array position 0 - 'ximageinfo->no'
581 int Read_PCX_to_Pixmap(Display *display, Window window, GC gc, char *filename,
582 Pixmap *pixmap, Pixmap *pixmap_mask)
585 XImageInfo *ximageinfo;
591 debug_print_timestamp(2, NULL); /* initialize timestamp function */
594 /* read the graphic file in PCX format to image structure */
595 if ((image = Read_PCX_to_Image(filename)) == NULL)
599 printf("%s:\n", filename);
600 debug_print_timestamp(2, " READING PCX FILE TO IMAGE: ");
603 screen = DefaultScreen(display);
604 visual = DefaultVisual(display, screen);
605 depth = DefaultDepth(display, screen);
607 /* convert image structure to X11 Pixmap */
608 if (!(ximageinfo = Image_to_Pixmap(display, screen, visual,
609 window, gc, depth, image)))
613 return PCX_OtherError;
616 /* if a private colormap has been created, install it */
617 if (ximageinfo->cmap != DefaultColormap(display, screen))
618 XSetWindowColormap(display, window, ximageinfo->cmap);
621 debug_print_timestamp(2, " CONVERTING IMAGE TO PIXMAP:");
624 /* create clip mask for the image */
625 ximageinfo->pixmap_mask = Image_to_Mask(image, display, window);
628 debug_print_timestamp(2, " CONVERTING IMAGE TO MASK: ");
631 *pixmap = ximageinfo->pixmap;
632 *pixmap_mask = ximageinfo->pixmap_mask;
634 /* free generic image and ximageinfo after native Pixmap has been created */
641 #endif /* PLATFORM_UNIX */
642 #endif /* TARGET_X11 */
645 /* ========================================================================= */
646 /* PLATFORM INDEPENDANT IMAGE FUNCTIONS */
647 /* ========================================================================= */
651 char *source_filename;
656 typedef struct ImageInfo ImageInfo;
658 static struct ArtworkListInfo *image_info = NULL;
660 static void *Load_PCX(char *filename)
665 printf("loading PCX file '%s'\n", filename);
668 img_info = checked_calloc(sizeof(ImageInfo));
670 if ((img_info->bitmap = LoadImage(filename)) == NULL)
672 Error(ERR_WARN, "cannot read image file '%s': LoadImage() failed: %s",
673 filename, GetError());
678 img_info->source_filename = getStringCopy(filename);
683 static void FreeImage(void *ptr)
685 ImageInfo *image = (ImageInfo *)ptr;
691 FreeBitmap(image->bitmap);
693 if (image->source_filename)
694 free(image->source_filename);
699 struct FileInfo *getCurrentImageList()
701 return image_info->file_list;
704 Bitmap *getBitmapFromImageID(int graphic)
706 ImageInfo **img_info = (ImageInfo **)image_info->artwork_list;
708 return (img_info[graphic] != NULL ? img_info[graphic]->bitmap : NULL);
711 char *getTokenFromImageID(int graphic)
713 struct FileInfo *file_list = (struct FileInfo *)image_info->file_list;
715 return file_list[graphic].token;
718 char *getImageConfigFilename()
720 return getCustomArtworkConfigFilename(image_info->type);
723 void InitImageList(struct ConfigInfo *config_list,
724 struct ConfigInfo *config_suffix_list,
725 int num_file_list_entries)
729 image_info = checked_calloc(sizeof(struct ArtworkListInfo));
731 image_info->type = ARTWORK_TYPE_GRAPHICS;
733 image_info->num_file_list_entries = num_file_list_entries;
734 image_info->num_suffix_list_entries = 0;
735 for (i=0; config_suffix_list[i].token != NULL; i++)
736 image_info->num_suffix_list_entries++;
738 image_info->file_list =
739 getFileListFromConfigList(config_list, config_suffix_list,
740 num_file_list_entries);
741 image_info->suffix_list = config_suffix_list;
743 image_info->artwork_list =
744 checked_calloc(num_file_list_entries * sizeof(ImageInfo *));
746 image_info->content_list = NULL;
748 image_info->load_artwork = Load_PCX;
749 image_info->free_artwork = FreeImage;
752 void ReloadCustomImages()
755 printf("DEBUG: reloading images '%s' ...\n", artwork.gfx_current_identifier);
758 ReloadCustomArtworkList(image_info);
763 FreeCustomArtworkList(image_info);