From cc47605531bf26c3884648d69fed17b44584c053 Mon Sep 17 00:00:00 2001 From: Ray Chason Date: Sun, 23 Aug 2026 09:25:38 -0400 Subject: [PATCH] Add BMP and GIF support --- include/tileset.h | 19 ++- include/winX.h | 1 + sys/unix/Makefile.src | 16 +- win/X11/winX.c | 3 +- win/X11/winmap.c | 367 ++++++++++++++++++++++++++++++++---------- win/share/tileset.c | 138 +++++++++------- 6 files changed, 396 insertions(+), 148 deletions(-) diff --git a/include/tileset.h b/include/tileset.h index 20c5fe6a7..f6f394ac8 100644 --- a/include/tileset.h +++ b/include/tileset.h @@ -9,6 +9,9 @@ struct Pixel { unsigned char r, g, b, a; }; +/* To let the tileset interface split the tiles into separate images, use + * these functions */ + struct TileImage { /* Image data */ unsigned width, height; @@ -22,12 +25,8 @@ void set_tile_type(boolean true_color); void free_tiles(void); const struct TileImage *get_tile(unsigned tile_index); -/* For resizing tiles */ -struct TileImage *stretch_tile(const struct TileImage *, unsigned, - unsigned); -void free_tile(struct TileImage *); +/* To read the tileset as a single image, use these functions */ -/* Used internally by the tile set code */ struct TileSetImage { /* Image data */ unsigned width, height; @@ -42,6 +41,16 @@ struct TileSetImage { unsigned tile_width, tile_height; }; +boolean read_tile_image(struct TileSetImage *image, + const char *filename, boolean true_color); +void free_tile_image(struct TileSetImage *image); + +/* For resizing tiles */ +struct TileImage *stretch_tile(const struct TileImage *, unsigned, + unsigned); +void free_tile(struct TileImage *); + +/* Used internally by the tile set code */ boolean read_bmp_tiles(const char *filename, struct TileSetImage *image); boolean read_gif_tiles(const char *filename, struct TileSetImage *image); boolean read_png_tiles(const char *filename, struct TileSetImage *image); diff --git a/include/winX.h b/include/winX.h index 88d61df9a..0133c3d44 100644 --- a/include/winX.h +++ b/include/winX.h @@ -390,6 +390,7 @@ extern void nh_XtPopup(Widget, int, Widget); extern void nh_XtPopdown(Widget); extern void win_X11_init(int); extern void find_scrollbars(Widget, Widget, Widget *, Widget *); +extern Boolean nhApproxColor(Screen *, Colormap, char *, XColor *); /* ### winmesg.c ### */ extern void set_message_slider(struct xwindow *); diff --git a/sys/unix/Makefile.src b/sys/unix/Makefile.src index cb669d80d..7064cef64 100644 --- a/sys/unix/Makefile.src +++ b/sys/unix/Makefile.src @@ -259,11 +259,14 @@ WINCURSESOBJ ?= $(TARGETPFX)cursmain.o $(TARGETPFX)curswins.o \ WINX11SRC ?= ../win/X11/Window.c ../win/X11/dialogs.c ../win/X11/winX.c \ ../win/X11/winmap.c ../win/X11/winmenu.c ../win/X11/winmesg.c \ ../win/X11/winmisc.c ../win/X11/winstat.c ../win/X11/wintext.c \ - ../win/X11/winval.c ../win/X11/winlabel.c $(GENTILECFILE) + ../win/X11/winval.c ../win/X11/winlabel.c \ + ../win/share/tileset.c ../win/share/bmptiles.c ../win/share/giftiles.c \ + $(GENTILECFILE) WINX11OBJ ?= $(TARGETPFX)Window.o $(TARGETPFX)dialogs.o $(TARGETPFX)winX.o \ $(TARGETPFX)winmap.o $(TARGETPFX)winmenu.o $(TARGETPFX)winmesg.o \ $(TARGETPFX)winmisc.o $(TARGETPFX)winstat.o $(TARGETPFX)wintext.o \ - $(TARGETPFX)winval.o $(TARGETPFX)winlabel.o + $(TARGETPFX)winval.o $(TARGETPFX)winlabel.o \ + $(TARGETPFX)tileset.o $(TARGETPFX)bmptiles.o $(TARGETPFX)giftiles.o # # Files for a Qt 3 interface (renamed since nethack 3.6.x) # @@ -1101,6 +1104,15 @@ $(TARGETPFX)wintext.o: ../win/X11/wintext.c $(HACK_H) ../include/winX.h \ $(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) -c -o $@ ../win/X11/wintext.c $(TARGETPFX)winval.o: ../win/X11/winval.c $(HACK_H) ../include/winX.h $(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) -c -o $@ ../win/X11/winval.c +$(TARGETPFX)tileset.o: ../win/share/tileset.c $(CONFIG_H) \ + ../include/objclass.h ../include/flag.h ../include/tileset.h + $(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) -c -o $@ ../win/share/tileset.c +$(TARGETPFX)bmptiles.o: ../win/share/bmptiles.c $(CONFIG_H) \ + ../include/objclass.h ../include/tileset.h + $(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) -c -o $@ ../win/share/bmptiles.c +$(TARGETPFX)giftiles.o: ../win/share/giftiles.c $(CONFIG_H) \ + ../include/objclass.h ../include/tileset.h + $(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) -c -o $@ ../win/share/giftiles.c $(TARGETPFX)wc_chainin.o: ../win/chain/wc_chainin.c $(HACK_H) $(TARGET_CC) $(TARGET_CFLAGS) -c -o $@ ../win/chain/wc_chainin.c $(TARGETPFX)wc_chainout.o: ../win/chain/wc_chainout.c $(HACK_H) diff --git a/win/X11/winX.c b/win/X11/winX.c index 07fccccdd..507cd138e 100644 --- a/win/X11/winX.c +++ b/win/X11/winX.c @@ -129,7 +129,6 @@ static XtSignalId X11_sig_id; #endif static void init_menu_nhcolors(struct xwindow *); -static Boolean nhApproxColor(Screen *, Colormap, char *, XColor *); static Boolean nhCvtStringToPixel(Display *, XrmValuePtr, Cardinal *, XrmValuePtr, XrmValuePtr, XtPointer *); static void get_window_frame_extents(Widget, long *, long *, long *, long *); @@ -460,7 +459,7 @@ static XtConvertArgRec const nhcolorConvertArgs[] = { * The approximate color found is returned in color as well. * Return True if something close was found. */ -static Boolean +Boolean nhApproxColor( Screen *screen, /* screen to use */ Colormap colormap, /* the colormap to use */ diff --git a/win/X11/winmap.c b/win/X11/winmap.c index 6e92eda63..be7fbcfdc 100644 --- a/win/X11/winmap.c +++ b/win/X11/winmap.c @@ -39,6 +39,7 @@ #include "hack.h" #include "dlb.h" #include "winX.h" +#include "tileset.h" #ifdef USE_XPM #include @@ -82,6 +83,9 @@ static XFontStruct *X11_get_map_font_struct(struct xwindow *wp); #endif static void get_text_gc(struct xwindow *, Font); static boolean init_tiles(struct xwindow *); +static unsigned long color_to_rgb(unsigned, unsigned, unsigned, + const Visual *); +static unsigned long shift_color(unsigned color, unsigned long mask); static void set_button_values(Widget, int, int, unsigned); static void map_check_size_change(struct xwindow *); static void map_update(struct xwindow *, int, int, int, int, boolean); @@ -92,6 +96,13 @@ static void map_all_unexplored(struct map_info_t *); static void get_char_info(struct xwindow *); static void display_cursor(struct xwindow *); +static boolean read_any_tiles(const char *tile_file); +#ifdef USE_XPM +static boolean read_xpm_tiles(const char *tile_file); +#else +static boolean read_binary_tiles(const char *tile_file); +#endif + /* Global functions ======================================================= */ void @@ -315,22 +326,6 @@ post_process_tiles(void) static boolean init_tiles(struct xwindow *wp) { -#ifdef USE_XPM - XpmAttributes attributes; - int errorcode; -#else - FILE *fp = (FILE *) 0; - x11_header header; - unsigned char *cp, *colormap = (unsigned char *) 0; - unsigned char *tb, *tile_bytes = (unsigned char *) 0; - int size; - XColor *colors = (XColor *) 0; - unsigned i; - int x, y; - int bitmap_pad; - int ddepth; -#endif - char buf[BUFSZ]; Display *dpy = XtDisplay(toplevel); Screen *screen = DefaultScreenOfDisplay(dpy); struct map_info_t *map_info = (struct map_info_t *) 0; @@ -339,53 +334,285 @@ init_tiles(struct xwindow *wp) boolean result = TRUE; XGCValues values; XtGCMask mask; + const char *tile_file; /* already have tile information */ if (tile_pixmap != None) goto tiledone; + /* honor the user's tile file setting */ + if (iflags.wc_tile_file != NULL && iflags.wc_tile_file[0] != '\0') { + tile_file = iflags.wc_tile_file; + } else { + tile_file = appResources.tile_file; + } + map_info = wp->map_information; tile_info = &map_info->tile_map; (void) memset((genericptr_t) tile_info, 0, sizeof (struct tile_map_info_t)); /* no tile file name, no tile information */ - if (!appResources.tile_file[0]) { + if (!tile_file[0]) { result = FALSE; goto tiledone; } + result = read_any_tiles(tile_file); + if (!result) { #ifdef USE_XPM + result = read_xpm_tiles(tile_file); + #else + result = read_binary_tiles(tile_file); +#endif + } + + if (!result) + goto tiledone; + + /* fake an inverted tile by drawing a border around the edges */ +#ifdef USE_WHITE + /* use white or black as the border */ + mask = GCFunction | GCForeground | GCGraphicsExposures; + values.graphics_exposures = False; + values.foreground = WhitePixelOfScreen(screen); + values.function = GXcopy; + tile_info->white_gc = XtGetGC(wp->w, mask, &values); + values.graphics_exposures = False; + values.foreground = BlackPixelOfScreen(screen); + values.function = GXcopy; + tile_info->black_gc = XtGetGC(wp->w, mask, &values); +#else + /* + * Use xor so we don't have to check for special colors. Xor white + * against the upper left pixel of the corridor so that we have a + * white rectangle when in a corridor. + */ + mask = GCFunction | GCForeground | GCGraphicsExposures; + values.graphics_exposures = False; + values.foreground = + WhitePixelOfScreen(screen) + ^ XGetPixel(tile_image, 0, + tile_height * T_corr); + values.function = GXxor; + tile_info->white_gc = XtGetGC(wp->w, mask, &values); + + mask = GCFunction | GCGraphicsExposures; + values.function = GXCopy; + values.graphics_exposures = False; + tile_info->black_gc = XtGetGC(wp->w, mask, &values); +#endif /* USE_WHITE */ + + tiledone: + if (result) { /* succeeded */ + tile_info->square_height = tile_height; + tile_info->square_width = tile_width; + tile_info->square_ascent = 0; + tile_info->square_lbearing = 0; + tile_info->image_width = image_width; + tile_info->image_height = image_height; + } + + return result; +} + +static boolean +read_any_tiles(const char *tile_file) +{ + Display *dpy = XtDisplay(toplevel); + int ddepth = DefaultDepth(dpy, DefaultScreen(dpy)); + + /* Read the image */ + struct TileSetImage image; + boolean result = read_tile_image(&image, tile_file, ddepth >= 15); + if (!result) { + /* Format not recognized */ + return FALSE; + } + + /* Set the tile dimensions */ + unsigned image_width = image.width; + unsigned image_height = image.height; + tile_width = (iflags.wc_tile_width != 0) + ? iflags.wc_tile_width + : (int) image.tile_width; + tile_height = (iflags.wc_tile_height != 0) + ? iflags.wc_tile_height + : (int) image.tile_height; + + /* Set the tile count */ + int tile_cols = image.width / tile_width; + int tile_rows = image.height / tile_height; + tile_count = tile_cols * tile_rows; + + /* Double the size if requested */ + if (appResources.double_tile_size) { + tile_width *= 2; + tile_height *= 2; + image_height *= 2; + image_width *= 2; + } + + /* calculate bitmap_pad */ + int bitmap_pad; + if (ddepth > 16) + bitmap_pad = 32; + else if (ddepth > 8) + bitmap_pad = 16; + else + bitmap_pad = 8; + + /* Convert to XImage */ + Screen *screen = DefaultScreenOfDisplay(dpy); + Visual *visual = DefaultVisualOfScreen(screen); + tile_image = XCreateImage( + dpy, visual, + ddepth, /* depth */ + ZPixmap, /* format */ + 0, /* offset */ + (char *) 0, /* data */ + image_width, /* width */ + image_height, /* height */ + bitmap_pad, /* bit pad */ + 0); /* bytes_per_line */ + + if (tile_image == NULL) { + X11_raw_print("Failed to allocate XImage"); + goto tile_error; + } + + /* now we know the physical memory requirements, we can allocate space */ + tile_image->data = + (char *) alloc((unsigned) tile_image->bytes_per_line * image_height); + + unsigned char *tile_bytes = image.indexes; + XColor colors[256]; + if (tile_bytes != NULL) { /* tileset uses a palette */ + size_t size = (size_t) image.width * (size_t) image.height; + unsigned num_colors = 0; + for (unsigned j = 0; j < size; ++j) { + num_colors = max(num_colors, tile_bytes[j] + 1U); + } + for (unsigned i = 0; i < num_colors; i++) { + struct Pixel *cp = &image.palette[i]; + colors[i].red = cp->r * 256; + colors[i].green = cp->g * 256; + colors[i].blue = cp->b * 256; + colors[i].flags = 0; + colors[i].pixel = 0; + + if (!XAllocColor(dpy, DefaultColormapOfScreen(screen), &colors[i]) + && !nhApproxColor(screen, DefaultColormapOfScreen(screen), + (char *) 0, &colors[i])) { + char buf[BUFSZ]; + Snprintf(buf, sizeof (buf), "%dth of %u color allocation failed", i, + num_colors); + X11_raw_print(buf); + goto tile_error; + } + } + } + + /* Convert the pixels */ + unsigned char *tb = image.indexes; + struct Pixel *cp = image.pixels; + for (unsigned y = 0; y < image.height; y++) { + for (unsigned x = 0; x < image.width; x++) { + unsigned long pixel; + if (tb == NULL) { + pixel = color_to_rgb(cp->r, cp->g, cp->b, visual); + ++cp; + } else { + pixel = colors[*tb++].pixel; + } + if (appResources.double_tile_size) { + XPutPixel(tile_image, x * 2 + 0, y * 2 + 0, pixel); + XPutPixel(tile_image, x * 2 + 1, y * 2 + 0, pixel); + XPutPixel(tile_image, x * 2 + 0, y * 2 + 1, pixel); + XPutPixel(tile_image, x * 2 + 1, y * 2 + 1, pixel); + } else { + XPutPixel(tile_image, x, y, pixel); + } + } + } + + free_tile_image(&image); + return TRUE; + +tile_error: + free_tile_image(&image); + return FALSE; +} + +static unsigned long +color_to_rgb(unsigned red, unsigned green, unsigned blue, const Visual *visual) +{ + return shift_color(red, visual->red_mask ) + | shift_color(green, visual->green_mask) + | shift_color(blue, visual->blue_mask ); +} + +static unsigned long +shift_color(unsigned color, unsigned long mask) +{ + unsigned long mask1; + unsigned long color1; + + /* Shift color to match mask */ + mask1 = mask; + color1 = color; + while (mask1 > 0xFF) { + mask1 >>= 1; + color1 <<= 1; + } + while (mask1 < 0x80) { + mask1 <<= 1; + color1 >>= 1; + } + + return mask & color1; +} + +#ifdef USE_XPM +static boolean +read_xpm_tiles(const char *tile_file) +{ + XpmAttributes attributes; + int errorcode; + + char buf[BUFSZ]; + Display *dpy = XtDisplay(toplevel); + unsigned int image_height = 0, image_width = 0; + attributes.valuemask = XpmCloseness; attributes.closeness = 25000; - errorcode = XpmReadFileToImage(dpy, appResources.tile_file, &tile_image, + errorcode = XpmReadFileToImage(dpy, tile_file, &tile_image, 0, &attributes); if (errorcode == XpmColorFailed) { Sprintf(buf, "Insufficient colors available to load %s.", - appResources.tile_file); + tile_file); X11_raw_print(buf); X11_raw_print("Try closing other colorful applications and restart."); X11_raw_print("Attempting to load with inferior colors."); attributes.closeness = 50000; - errorcode = XpmReadFileToImage(dpy, appResources.tile_file, + errorcode = XpmReadFileToImage(dpy, tile_file, &tile_image, 0, &attributes); } if (errorcode != XpmSuccess) { if (errorcode == XpmColorFailed) { Sprintf(buf, "Insufficient colors available to load %s.", - appResources.tile_file); + tile_file); X11_raw_print(buf); } else { - Sprintf(buf, "Failed to load %s: %s", appResources.tile_file, + Sprintf(buf, "Failed to load %s: %s", tile_file, XpmGetErrorString(errorcode)); X11_raw_print(buf); } - result = FALSE; X11_raw_print("Switching to text-based mode."); - goto tiledone; + return FALSE; } /* assume a fixed number of tiles per row */ @@ -393,12 +620,11 @@ init_tiles(struct xwindow *wp) || tile_image->width <= TILES_PER_ROW) { Sprintf(buf, "%s is not a multiple of %d (number of tiles/row) pixels wide", - appResources.tile_file, TILES_PER_ROW); + tile_file, TILES_PER_ROW); X11_raw_print(buf); XDestroyImage(tile_image); tile_image = 0; - result = FALSE; - goto tiledone; + return FALSE; } /* infer tile dimensions from image size and TILES_PER_ROW */ @@ -411,25 +637,47 @@ init_tiles(struct xwindow *wp) } tile_width = image_width / TILES_PER_ROW; tile_height = image_height / (tile_count / TILES_PER_ROW); + + return TRUE; +} + #else /* !USE_XPM */ + +static boolean +read_binary_tiles(const char *tile_file) +{ + FILE *fp = (FILE *) 0; + x11_header header; + unsigned char *cp, *colormap = (unsigned char *) 0; + unsigned char *tb, *tile_bytes = (unsigned char *) 0; + int size; + XColor *colors = (XColor *) 0; + unsigned i; + int x, y; + int bitmap_pad; + int ddepth; + + char buf[BUFSZ]; + Display *dpy = XtDisplay(toplevel); + Screen *screen = DefaultScreenOfDisplay(dpy); + unsigned int image_height = 0, image_width = 0; + boolean result = FALSE; + /* any less than 16 colours makes tiles useless */ ddepth = DefaultDepthOfScreen(screen); if (ddepth < 4) { X11_raw_print("need a screen depth of at least 4"); - result = FALSE; goto tiledone; } - fp = fopen_datafile(appResources.tile_file, RDBMODE, FALSE); + fp = fopen_datafile(tile_file, RDBMODE, FALSE); if (!fp) { X11_raw_print("can't open tile file"); - result = FALSE; goto tiledone; } if ((int) fread((char *) &header, sizeof(header), 1, fp) != 1) { X11_raw_print("read of header failed"); - result = FALSE; goto tiledone; } @@ -437,7 +685,6 @@ init_tiles(struct xwindow *wp) Sprintf(buf, "Wrong tile file version, expected 2, got %lu", header.version); X11_raw_print(buf); - result = FALSE; goto tiledone; } #ifdef VERBOSE @@ -453,7 +700,6 @@ ntiles %ld\n", colormap = (unsigned char *) alloc((unsigned) size); if ((int) fread((char *) colormap, 1, size, fp) != size) { X11_raw_print("read of colormap failed"); - result = FALSE; goto tiledone; } @@ -472,7 +718,6 @@ ntiles %ld\n", Sprintf(buf, "%dth out of %ld color allocation failed", i, header.ncolors); X11_raw_print(buf); - result = FALSE; goto tiledone; } } @@ -490,7 +735,6 @@ ntiles %ld\n", if ((int) fread((char *) tile_bytes, size, tile_count, fp) != tile_count) { X11_raw_print("read of tile bytes failed"); - result = FALSE; goto tiledone; } @@ -498,7 +742,6 @@ ntiles %ld\n", Sprintf(buf, "tile file incomplete, expecting %d tiles, found %lu", total_tiles_used, header.ntiles); X11_raw_print(buf); - result = FALSE; goto tiledone; } @@ -534,7 +777,6 @@ ntiles %ld\n", if (!tile_image) { impossible("init_tiles: insufficient memory to create image"); X11_raw_print("Resorting to text map."); - result = FALSE; goto tiledone; } @@ -566,47 +808,10 @@ ntiles %ld\n", for (x = 0; x < (int) image_width; x++, tb++) XPutPixel(tile_image, x, y, colors[*tb].pixel); } -#endif /* ?USE_XPM */ - /* fake an inverted tile by drawing a border around the edges */ -#ifdef USE_WHITE - /* use white or black as the border */ - mask = GCFunction | GCForeground | GCGraphicsExposures; - values.graphics_exposures = False; - values.foreground = WhitePixelOfScreen(screen); - values.function = GXcopy; - tile_info->white_gc = XtGetGC(wp->w, mask, &values); - values.graphics_exposures = False; - values.foreground = BlackPixelOfScreen(screen); - values.function = GXcopy; - tile_info->black_gc = XtGetGC(wp->w, mask, &values); -#else - /* - * Use xor so we don't have to check for special colors. Xor white - * against the upper left pixel of the corridor so that we have a - * white rectangle when in a corridor. - */ - mask = GCFunction | GCForeground | GCGraphicsExposures; - values.graphics_exposures = False; - values.foreground = - WhitePixelOfScreen(screen) - ^ XGetPixel(tile_image, 0, -#if 0 - tile_height * glyph2tile[cmap_to_glyph(S_corr)]); -#else - tile_height * T_corr); -#endif - values.function = GXxor; - tile_info->white_gc = XtGetGC(wp->w, mask, &values); - - mask = GCFunction | GCGraphicsExposures; - values.function = GXCopy; - values.graphics_exposures = False; - tile_info->black_gc = XtGetGC(wp->w, mask, &values); -#endif /* USE_WHITE */ + result = TRUE; tiledone: -#ifndef USE_XPM if (fp) (void) fclose(fp); if (colormap) @@ -615,20 +820,12 @@ ntiles %ld\n", free((genericptr_t) tile_bytes); if (colors) free((genericptr_t) colors); -#endif - - if (result) { /* succeeded */ - tile_info->square_height = tile_height; - tile_info->square_width = tile_width; - tile_info->square_ascent = 0; - tile_info->square_lbearing = 0; - tile_info->image_width = image_width; - tile_info->image_height = image_height; - } return result; } +#endif /* ?USE_XPM */ + /* * Make sure the map's cursor is always visible. */ diff --git a/win/share/tileset.c b/win/share/tileset.c index 556298a7c..355b6b210 100644 --- a/win/share/tileset.c +++ b/win/share/tileset.c @@ -10,10 +10,15 @@ #include "flag.h" #include "tileset.h" +/* Define this for systems that use the tile-splitting functions */ +#if defined(MSDOS) +#define USE_TILE_SPLITTING +#endif + static void get_tile_map(const char *); +#ifdef USE_TILE_SPLITTING static unsigned gcd(unsigned, unsigned); static void split_tiles(const struct TileSetImage *); -static void free_image(struct TileSetImage *); static struct TileImage *tiles; static unsigned num_tiles; @@ -25,25 +30,57 @@ static struct Pixel palette[256]; boolean read_tiles(const char *filename, boolean true_color) { - static const unsigned char png_sig[] = { - 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A - }; struct TileSetImage image; - FILE *fp; - char header[16]; - boolean ok; if (tiles != NULL) return TRUE; + if (!read_tile_image(&image, filename, true_color)) { + return FALSE; + } + + /* Save the palette if present */ + have_palette = image.indexes != NULL; + memcpy(palette, image.palette, sizeof(palette)); + + /* Set the tile dimensions if the user has not done so */ + if (iflags.wc_tile_width == 0) { + iflags.wc_tile_width = image.tile_width; + } + if (iflags.wc_tile_height == 0) { + iflags.wc_tile_height = image.tile_height; + } + + /* Split the image into tiles */ + split_tiles(&image); + + free_tile_image(&image); + return TRUE; +} +#endif /* USE_TILE_SPLITTING */ + +boolean +read_tile_image(struct TileSetImage *image, const char *filename, boolean true_color) +{ + static const unsigned char png_sig[] = { + 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A + }; + FILE *fp; + char header[16]; + boolean ok; + /* Fill the image structure with known values */ - image.width = 0; - image.height = 0; - image.pixels = NULL; /* custodial */ - image.indexes = NULL; /* custodial */ - image.image_desc = NULL; /* custodial */ - image.tile_width = 0; - image.tile_height = 0; + image->width = 0; + image->height = 0; + image->pixels = NULL; /* custodial, returned */ + image->indexes = NULL; /* custodial, returned */ + image->image_desc = NULL; /* custodial, returned */ + image->tile_width = 0; + image->tile_height = 0; + + if (filename == NULL || filename[0] == '\0') { + filename = "nhtiles.bmp"; + } /* Identify the image type */ fp = fopen(filename, "rb"); @@ -55,12 +92,12 @@ read_tiles(const char *filename, boolean true_color) /* Call the loader appropriate for the image */ if (memcmp(header, "BM", 2) == 0) { - ok = read_bmp_tiles(filename, &image); + ok = read_bmp_tiles(filename, image); } else if (memcmp(header, "GIF87a", 6) == 0 || memcmp(header, "GIF89a", 6) == 0) { - ok = read_gif_tiles(filename, &image); + ok = read_gif_tiles(filename, image); } else if (memcmp(header, png_sig, sizeof(png_sig)) == 0) { - ok = read_png_tiles(filename, &image); + ok = read_png_tiles(filename, image); } else { ok = FALSE; } @@ -69,44 +106,30 @@ read_tiles(const char *filename, boolean true_color) /* Reject if the interface cannot handle direct color and the image does not use a palette */ - if (!true_color && image.indexes == NULL) + if (!true_color && image->indexes == NULL) goto error; - /* Save the palette if present */ - have_palette = image.indexes != NULL; - memcpy(palette, image.palette, sizeof(palette)); + /* Parse the tile map */ + get_tile_map(image->image_desc); /* Set defaults for tile metadata */ - if (image.tile_width == 0) { - image.tile_width = image.width / 40; + if (image->tile_width == 0) { + image->tile_width = image->width / 40; } - if (image.tile_height == 0) { - image.tile_height = image.tile_width; - } - /* Set the tile dimensions if the user has not done so */ - if (iflags.wc_tile_width == 0) { - iflags.wc_tile_width = image.tile_width; - } - if (iflags.wc_tile_height == 0) { - iflags.wc_tile_height = image.tile_height; + if (image->tile_height == 0) { + image->tile_height = image->tile_width; } - /* Parse the tile map */ - get_tile_map(image.image_desc); - - /* Split the image into tiles */ - split_tiles(&image); - - free_image(&image); return TRUE; error: if (fp) fclose(fp); - free_image(&image); + free_tile_image(image); return FALSE; } +#ifdef USE_TILE_SPLITTING /* Free tile memory not required by the chosen display mode */ void set_tile_type(boolean true_color) @@ -134,6 +157,7 @@ get_palette(void) { return have_palette ? palette : NULL; } +#endif /* USE_TILE_SPLITTING */ /* TODO: derive tile_map from image_desc */ static void @@ -142,6 +166,7 @@ get_tile_map(const char *image_desc UNUSED) return; } +#ifdef USE_TILE_SPLITTING void free_tiles(void) { @@ -161,9 +186,10 @@ free_tiles(void) if (blank_tile.indexes) free((genericptr_t) blank_tile.indexes), blank_tile.indexes = NULL; } +#endif /* USE_TILE_SPLITTING */ -static void -free_image(struct TileSetImage *image) +void +free_tile_image(struct TileSetImage *image) { if (image->pixels) free((genericptr_t) image->pixels), image->pixels = NULL; @@ -173,6 +199,7 @@ free_image(struct TileSetImage *image) free((genericptr_t) image->image_desc), image->image_desc = NULL; } +#ifdef USE_TILE_SPLITTING const struct TileImage * get_tile(unsigned tile_index) { @@ -292,10 +319,10 @@ split_tiles(const struct TileSetImage *image) unsigned x1, y1, y2; /* Get the number of tiles */ - tile_rows = image->height / iflags.wc_tile_height; - tile_cols = image->width / iflags.wc_tile_width; + tile_rows = image->height / image->tile_height; + tile_cols = image->width / image->tile_width; num_tiles = tile_rows * tile_cols; - tile_size = (size_t) iflags.wc_tile_height * (size_t) iflags.wc_tile_width; + tile_size = (size_t) image->tile_height * (size_t) image->tile_width; /* Allocate the tile array */ tiles = (struct TileImage *) alloc(num_tiles * sizeof(tiles[0])); @@ -306,31 +333,31 @@ split_tiles(const struct TileSetImage *image) for (x1 = 0; x1 < tile_cols; ++x1) { struct TileImage *tile = &tiles[y1 * tile_cols + x1]; - tile->width = iflags.wc_tile_width; - tile->height = iflags.wc_tile_height; + tile->width = image->tile_width; + tile->height = image->tile_height; tile->pixels = (struct Pixel *) alloc(tile_size * sizeof (struct Pixel)); if (image->indexes != NULL) { tile->indexes = (unsigned char *) alloc(tile_size); } - for (y2 = 0; y2 < (unsigned) iflags.wc_tile_height; ++y2) { - unsigned y = y1 * iflags.wc_tile_height + y2; - unsigned x = x1 * iflags.wc_tile_width; + for (y2 = 0; y2 < (unsigned) image->tile_height; ++y2) { + unsigned y = y1 * image->tile_height + y2; + unsigned x = x1 * image->tile_width; i = y * image->width; j = y2 * tile->width; memcpy(tile->pixels + j, image->pixels + i + x, - sizeof(tile->pixels[0]) * iflags.wc_tile_width); + sizeof(tile->pixels[0]) * image->tile_width); if (image->indexes != NULL) { memcpy(tile->indexes + j, image->indexes + i + x, - iflags.wc_tile_width); + image->tile_width); } } } } /* Create a blank tile for use when the tile index is invalid */ - blank_tile.width = iflags.wc_tile_width; - blank_tile.height = iflags.wc_tile_height; + blank_tile.width = image->tile_width; + blank_tile.height = image->tile_height; blank_tile.pixels = (struct Pixel *) alloc(tile_size * sizeof (struct Pixel)); for (i = 0; i < tile_size; ++i) { @@ -344,12 +371,15 @@ split_tiles(const struct TileSetImage *image) memset(blank_tile.indexes, 0, tile_size); } } +#endif /* USE_TILE_SPLITTING */ +#ifndef USE_PNG boolean read_png_tiles(const char *filename UNUSED, struct TileSetImage *image UNUSED) { /* stub */ return FALSE; } +#endif /* !USE_PNG */ #endif /* TILES_IN_GLYPHMAP */