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 int getImageListSize()
701 return (image_info->num_file_list_entries +
702 image_info->num_dynamic_file_list_entries);
705 struct FileInfo *getImageListEntry(int pos)
707 int num_list_entries = image_info->num_file_list_entries;
708 int list_pos = (pos < num_list_entries ? pos : pos - num_list_entries);
710 return (pos < num_list_entries ? &image_info->file_list[list_pos] :
711 &image_info->dynamic_file_list[list_pos]);
714 Bitmap *getBitmapFromImageID(int pos)
716 int num_list_entries = image_info->num_file_list_entries;
717 int list_pos = (pos < num_list_entries ? pos : pos - num_list_entries);
718 ImageInfo **img_info =
719 (ImageInfo **)(pos < num_list_entries ? image_info->artwork_list :
720 image_info->dynamic_artwork_list);
722 return (img_info[list_pos] != NULL ? img_info[list_pos]->bitmap : NULL);
725 char *getTokenFromImageID(int graphic)
727 struct FileInfo *file_list = (struct FileInfo *)image_info->file_list;
729 return file_list[graphic].token;
732 char *getImageConfigFilename()
734 return getCustomArtworkConfigFilename(image_info->type);
737 int getImageListPropertyMappingSize()
739 return image_info->num_property_mapping_entries;
742 struct PropertyMapping *getImageListPropertyMapping()
744 return image_info->property_mapping;
747 void InitImageList(struct ConfigInfo *config_list, int num_file_list_entries,
748 struct ConfigInfo *config_suffix_list,
749 char **base_prefixes,
750 char **ext1_suffixes,
751 char **ext2_suffixes)
755 image_info = checked_calloc(sizeof(struct ArtworkListInfo));
756 image_info->type = ARTWORK_TYPE_GRAPHICS;
758 /* ---------- initialize file list and suffix lists ---------- */
760 image_info->num_file_list_entries = num_file_list_entries;
761 image_info->num_dynamic_file_list_entries = 0;
763 image_info->file_list =
764 getFileListFromConfigList(config_list, config_suffix_list,
765 num_file_list_entries);
766 image_info->dynamic_file_list = NULL;
768 image_info->num_suffix_list_entries = 0;
769 for (i=0; config_suffix_list[i].token != NULL; i++)
770 image_info->num_suffix_list_entries++;
772 image_info->suffix_list = config_suffix_list;
774 /* ---------- initialize base prefix and suffixes lists ---------- */
776 image_info->num_base_prefixes = 0;
777 for (i=0; base_prefixes[i] != NULL; i++)
778 image_info->num_base_prefixes++;
780 image_info->num_ext1_suffixes = 0;
781 for (i=0; ext1_suffixes[i] != NULL; i++)
782 image_info->num_ext1_suffixes++;
784 image_info->num_ext2_suffixes = 0;
785 for (i=0; ext2_suffixes[i] != NULL; i++)
786 image_info->num_ext2_suffixes++;
788 image_info->base_prefixes = base_prefixes;
789 image_info->ext1_suffixes = ext1_suffixes;
790 image_info->ext2_suffixes = ext2_suffixes;
792 image_info->num_property_mapping_entries = 0;
794 image_info->property_mapping = NULL;
796 /* ---------- initialize artwork reference and content lists ---------- */
798 image_info->sizeof_artwork_list_entry = sizeof(ImageInfo *);
800 image_info->artwork_list =
801 checked_calloc(num_file_list_entries * sizeof(ImageInfo *));
802 image_info->dynamic_artwork_list = NULL;
804 image_info->content_list = NULL;
806 /* ---------- initialize artwork loading/freeing functions ---------- */
808 image_info->load_artwork = Load_PCX;
809 image_info->free_artwork = FreeImage;
812 void ReloadCustomImages()
815 printf("DEBUG: reloading images '%s' ...\n", artwork.gfx_current_identifier);
818 LoadArtworkConfig(image_info);
819 ReloadCustomArtworkList(image_info);
824 FreeCustomArtworkLists(image_info);