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 9de766166..dcd15d049 100644 --- a/sys/unix/Makefile.src +++ b/sys/unix/Makefile.src @@ -259,11 +259,15 @@ 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 \ + ../win/share/pngtiles.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 \ + $(TARGETPFX)pngtiles.o # # Files for a Qt 3 interface (renamed since nethack 3.6.x) # @@ -1103,6 +1107,18 @@ $(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)pngtiles.o: ../win/share/pngtiles.c $(CONFIG_H) \ + ../include/objclass.h ../include/tileset.h + $(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) -c -o $@ ../win/share/pngtiles.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/sys/unix/hints/linux.500 b/sys/unix/hints/linux.500 index 656e831e6..b81a37517 100755 --- a/sys/unix/hints/linux.500 +++ b/sys/unix/hints/linux.500 @@ -398,6 +398,7 @@ endif # MAKEFILE_SRC ifdef WANT_WIN_X11 USE_XPM=1 USE_XFT=1 +USE_PNG=1 WINX11LIB = -lXaw -lXmu -lXext -lXt -lX11 VARDATND0 += x11tiles NetHack.ad pet_mark.xbm pilemark.xbm ifndef CROSSCOMPILE @@ -419,6 +420,10 @@ CFLAGS += -DUSE_XPM WINX11LIB += -lXpm VARDATND0 += rip.xpm endif +ifdef USE_PNG +CFLAGS += -DUSE_PNG +WINX11LIB += -lpng +endif WINLIB += $(WINX11LIB) LFLAGS+=-L/opt/X11/lib endif # WANT_WIN_X11 diff --git a/sys/unix/hints/macOS.500 b/sys/unix/hints/macOS.500 index 97d82388a..3fd074841 100755 --- a/sys/unix/hints/macOS.500 +++ b/sys/unix/hints/macOS.500 @@ -279,6 +279,7 @@ endif # WANT_WIN_CHAIN ifdef WANT_WIN_X11 USE_XFT=1 USE_XPM=1 +USE_PNG=1 WINX11LIB = -lXaw -lXmu -lXext -lXt -lX11 VARDATND0 += x11tiles NetHack.ad pet_mark.xbm pilemark.xbm GENTILEOFILE = $(SRCDIR)/tile.o @@ -298,6 +299,10 @@ CFLAGS += -DUSE_XPM WINX11LIB += -lXpm VARDATND0 += rip.xpm endif +ifdef USE_PNG +CFLAGS += -DUSE_PNG +WINX11LIB += -lpng +endif WINLIB += $(WINX11LIB) LFLAGS += -L/opt/X11/lib endif # WANT_WIN_X11 diff --git a/win/X11/winX.c b/win/X11/winX.c index 07fccccdd..e66172f5b 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 */ @@ -2793,7 +2792,7 @@ init_standard_windows(void) * Resize to at most full-screen. */ { -#define TITLEBAR_SPACE 18 /* Leave SOME screen for window decorations */ +#define TITLEBAR_SPACE 100 /* Leave SOME screen for window decorations and task bar */ int screen_width = WidthOfScreen(XtScreen(wp->w)); int screen_height = HeightOfScreen(XtScreen(wp->w)) - TITLEBAR_SPACE; diff --git a/win/X11/winmap.c b/win/X11/winmap.c index 6e92eda63..0d2c6b709 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,16 +83,27 @@ 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); static void init_text(struct xwindow *); +static void report_callback(Widget, XtPointer, XtPointer); static void map_exposed(Widget, XtPointer, XtPointer); static void set_gc(Widget, Font, const char *, Pixel, GC *, GC *); 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 +327,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 +335,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 +621,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 +638,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 +686,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 +701,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 +719,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 +736,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 +743,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 +778,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 +809,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 +821,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. */ @@ -1223,6 +1421,21 @@ set_button_values(Widget w, int x, int y, unsigned int button) click_button = (button == Button1) ? CLICK_1 : CLICK_2; } +/* This ensures that the cursor is visible when we first see the map */ +static void +report_callback(Widget w, XtPointer client_data, + XtPointer widget_data) +{ + nhUse(w); + XawPannerReport *report = (XawPannerReport *) widget_data; + if (report->changed & (XawPRSliderWidth | XawPRSliderHeight)) { + struct xwindow *wp = (struct xwindow *) client_data; + if (wp->map_information != NULL) { + check_cursor_visibility(wp); + } + } +} + /* * Map window expose callback. */ @@ -1793,6 +2006,7 @@ create_map_window( num_args); /* number of values to set */ XtAddCallback(map, XtNexposeCallback, map_exposed, (XtPointer) 0); + XtAddCallback(viewport, XtNreportCallback, report_callback, wp); map_info = wp->map_information = (struct map_info_t *) alloc(sizeof (struct map_info_t)); diff --git a/win/share/pngtiles.c b/win/share/pngtiles.c new file mode 100644 index 000000000..0c77d399f --- /dev/null +++ b/win/share/pngtiles.c @@ -0,0 +1,150 @@ +#include "config.h" +#include "tileset.h" + +#ifdef USE_PNG + +#include + +boolean +read_png_tiles(const char *filename, struct TileSetImage *image) +{ + png_structp read_png_ptr = NULL; /* custodial */ + png_infop read_info_ptr = NULL; /* custodial */ + png_infop read_end_info = NULL; /* custodial */ + + FILE *fp; + unsigned char header[8]; + size_t size, num_pixels; + unsigned x, y; + png_bytepp pixels; + png_byte color_type; + int palette_size = 0; + png_textp text_chunks; + int num_text_chunks, i; + + image->width = 0; + image->height = 0; + image->indexes = NULL; /* custodial, returned */ + image->pixels = NULL; /* custodial, returned */ + image->image_desc = NULL; /* custodial, returned */ + image->tile_width = 0; + image->tile_height = 0; + + /* Open the file */ + fp = fopen(filename, "rb"); + if (fp == NULL) goto error; + + /* Check for PNG format */ + memset(header, 0, sizeof(header)); + size = fread(header, 1, sizeof(header), fp); + if (size < sizeof(header)) goto error; + if (png_sig_cmp(header, 0, sizeof(header)) != 0) goto error; + + /* Create read info structures */ + read_png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, + NULL, NULL); + if (read_png_ptr == NULL) goto error; + if (setjmp(png_jmpbuf(read_png_ptr)) != 0) goto error; + read_info_ptr = png_create_info_struct(read_png_ptr); + read_end_info = png_create_info_struct(read_png_ptr); + + /* Link the file we opened to the read info */ + png_init_io(read_png_ptr, fp); + png_set_sig_bytes(read_png_ptr, sizeof(header)); + + /* Read the image */ + png_read_png(read_png_ptr, read_info_ptr, + PNG_TRANSFORM_PACKING | PNG_TRANSFORM_STRIP_16, NULL); + + pixels = png_get_rows(read_png_ptr, read_info_ptr); + image->width = png_get_image_width(read_png_ptr, read_info_ptr); + image->height = png_get_image_height(read_png_ptr, read_info_ptr); + color_type = png_get_color_type(read_png_ptr, read_info_ptr); + + /* Read the palette, if there is one */ + if (color_type & PNG_COLOR_MASK_PALETTE) { + png_colorp palette = NULL; + png_bytep trans_alpha; + int num_trans; + + png_get_PLTE(read_png_ptr, read_info_ptr, &palette, &palette_size); + for (i = 0; i < 256; ++i) { + image->palette[i].r = palette[i].red; + image->palette[i].g = palette[i].green; + image->palette[i].b = palette[i].blue; + image->palette[i].a = 255; + } + if (png_get_tRNS(read_png_ptr, read_info_ptr, &trans_alpha, &num_trans, + NULL) != 0) { + for (i = 0; i < num_trans; ++i) { + image->palette[i].a = trans_alpha[i]; + } + } + } + + /* Allocate pixel area; watch out for overflow */ + num_pixels = (size_t) image->width * (size_t) image->height; + if (num_pixels / image->width != image->height) goto error; /* overflow */ + size = num_pixels * sizeof(image->pixels[0]); + if (size / sizeof(image->pixels[0]) != num_pixels) goto error; /* overflow */ + image->pixels = (struct Pixel *) alloc(size); + if (color_type & PNG_COLOR_MASK_PALETTE) { + image->indexes = (unsigned char *) alloc(num_pixels); + } + + /* Convert the pixels */ + for (y = 0; y < image->height; ++y) { + png_bytep png_row = pixels[y]; + size_t index = y * image->width; + for (x = 0; x < image->width; ++x) { + if (color_type & PNG_COLOR_MASK_PALETTE) { + png_byte b = *png_row++; + image->indexes[index] = b; + image->pixels[index] = image->palette[b]; + } else { + if (color_type & PNG_COLOR_MASK_COLOR) { + image->pixels[index].r = *png_row++; + image->pixels[index].g = *png_row++; + image->pixels[index].b = *png_row++; + } else { + image->pixels[index].r = *png_row++; + image->pixels[index].g = image->pixels[index].r; + image->pixels[index].b = image->pixels[index].r; + } + if (color_type & PNG_COLOR_MASK_ALPHA) { + image->pixels[index].a = *png_row++; + } else { + image->pixels[index].a = 255; + } + } + ++index; + } + } + + /* Look for the tile information */ + png_get_text(read_png_ptr, read_info_ptr, &text_chunks, &num_text_chunks); + for (i = 0; i < num_text_chunks; ++i) { + if (strcmp(text_chunks[i].key, "NH_image_desc") == 0) { + image->image_desc = (char *) alloc(text_chunks[i].text_length + 1); + memcpy(image->image_desc, text_chunks[i].text, text_chunks[i].text_length); + text_chunks[i].text[text_chunks[i].text_length] = '\0'; + } + } + + png_destroy_read_struct(&read_png_ptr, &read_info_ptr, &read_end_info); + return 1; + +error: + if (read_png_ptr) { + png_destroy_read_struct(&read_png_ptr, &read_info_ptr, &read_end_info); + } + free(image->indexes); + image->indexes = NULL; + free(image->pixels); + image->pixels = NULL; + free(image->image_desc); + image->image_desc = NULL; + return 0; +} + +#endif 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 */