From a575fccf08e031a8725e8d7b19e9e88d8341757e Mon Sep 17 00:00:00 2001 From: Ray Chason Date: Sun, 23 Aug 2026 10:32:27 -0400 Subject: [PATCH] Add PNG support --- sys/unix/Makefile.src | 8 ++- sys/unix/hints/linux.500 | 5 ++ sys/unix/hints/macOS.500 | 5 ++ win/share/pngtiles.c | 150 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 166 insertions(+), 2 deletions(-) create mode 100644 win/share/pngtiles.c diff --git a/sys/unix/Makefile.src b/sys/unix/Makefile.src index 7064cef64..1def499ba 100644 --- a/sys/unix/Makefile.src +++ b/sys/unix/Makefile.src @@ -261,12 +261,13 @@ WINX11SRC ?= ../win/X11/Window.c ../win/X11/dialogs.c ../win/X11/winX.c \ ../win/X11/winmisc.c ../win/X11/winstat.c ../win/X11/wintext.c \ ../win/X11/winval.c ../win/X11/winlabel.c \ ../win/share/tileset.c ../win/share/bmptiles.c ../win/share/giftiles.c \ - $(GENTILECFILE) + ../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)tileset.o $(TARGETPFX)bmptiles.o $(TARGETPFX)giftiles.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) # @@ -1113,6 +1114,9 @@ $(TARGETPFX)bmptiles.o: ../win/share/bmptiles.c $(CONFIG_H) \ $(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/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