From 6512ac7dcf189d2682cf818c1e9a53a091c42107 Mon Sep 17 00:00:00 2001 From: Emil Kosz Date: Wed, 24 Sep 2025 20:07:10 +0200 Subject: [PATCH] Loading maps using libpng instead of SDL_image --- Makefile | 12 ++-- shell.nix | 2 +- src/client/client.c | 2 +- src/client/gfx_context.c | 2 +- src/client/gfx_context.h | 4 +- src/common/world.c | 138 ++++++++++++++++++++++++++------------- src/common/world.h | 8 +-- 7 files changed, 107 insertions(+), 61 deletions(-) diff --git a/Makefile b/Makefile index 1d0caee..fd8b00c 100644 --- a/Makefile +++ b/Makefile @@ -22,7 +22,7 @@ RM := rm -f DEBUG ?= 1 COMMON_CFLAGS := -std=$(STD) -Werror -Wextra -Wall -MMD -MP -I$(SRC_DIR) `pkg-config --cflags glib-2.0 gio-2.0` -COMMON_LDFLAGS := -std=$(STD) -lm `pkg-config --libs glib-2.0 gio-2.0 sdl3 sdl3-image` +COMMON_LDFLAGS := -std=$(STD) -lm `pkg-config --libs glib-2.0 gio-2.0` ifeq ($(DEBUG),1) COMMON_CFLAGS += -g -DDEBUG @@ -31,12 +31,12 @@ else COMMON_CFLAGS += -O2 -DNDEBUG endif +# libpng should be gone from the client after the server is working +CLIENT_CFLAGS := $(COMMON_CFLAGS) `pkg-config --cflags sdl3 libpng` +CLIENT_LDFLAGS := $(COMMON_LDFLAGS) `pkg-config --libs sdl3 libpng` -CLIENT_CFLAGS := $(COMMON_CFLAGS) # `pkg-config --cflags sdl3` -CLIENT_LDFLAGS := $(COMMON_LDFLAGS) # `pkg-config --libs sdl3` - -SERVER_CFLAGS := $(COMMON_CFLAGS) -SERVER_LDFLAGS := $(COMMON_LDFLAGS) +SERVER_CFLAGS := $(COMMON_CFLAGS) `pkg-config --cflags libpng` +SERVER_LDFLAGS := $(COMMON_LDFLAGS) `pkg-config --libs libpng` CLIENT := librefront SERVER := librefront-headless diff --git a/shell.nix b/shell.nix index 024f2e5..48b818b 100644 --- a/shell.nix +++ b/shell.nix @@ -5,7 +5,7 @@ in pkgs.mkShellNoCC { packages = with pkgs; [ sdl3 - sdl3-image + libpng glib gcc diff --git a/src/client/client.c b/src/client/client.c index 4026c47..29835ae 100644 --- a/src/client/client.c +++ b/src/client/client.c @@ -36,7 +36,7 @@ static World *test_world; World * _make_test_World () { - World *self = World_load_from_file ("res/test_map_2.png"); + World *self = World_load_from_path ("res/test_map_2.png"); /* TODO: add players and so on. That will be in GameState, though. Or GameData. */ diff --git a/src/client/gfx_context.c b/src/client/gfx_context.c index 3b92ab4..02ea7eb 100644 --- a/src/client/gfx_context.c +++ b/src/client/gfx_context.c @@ -55,7 +55,7 @@ GfxContext_render (GfxContext *self) { SDL_RenderTexture (self->renderer, self->world_texture, NULL, NULL); } - + SDL_RenderPresent (self->renderer); } diff --git a/src/client/gfx_context.h b/src/client/gfx_context.h index 413bc94..e839b6e 100644 --- a/src/client/gfx_context.h +++ b/src/client/gfx_context.h @@ -41,8 +41,8 @@ typedef struct GfxContext } GfxContext; GfxContext *GfxContext_new (); -void GfxContext_render (GfxContext *self); +void GfxContext_render (GfxContext * self); void GfxContext_free (GfxContext * self); void GfxContext_cache_World (GfxContext * self, World * world); -void GfxContext_update_Players (GfxContext *self, World * world); +void GfxContext_update_Players (GfxContext * self, World * world); diff --git a/src/common/world.c b/src/common/world.c index aebad54..c8e6ff5 100644 --- a/src/common/world.c +++ b/src/common/world.c @@ -1,69 +1,113 @@ #include -static inline Uint32 -_surf_pixel (SDL_Surface *surface, int x, int y) +typedef struct { - int bpp = SDL_BYTESPERPIXEL (surface->format); - Uint8 *p = (Uint8 *) surface->pixels + y * surface->pitch + x * bpp; + const guchar *data; + gsize size; + gsize offset; +} PngBuffer; - switch (bpp) - { - case 1: - return *p; - case 2: - return *(Uint16 *) p; - case 3: - if (SDL_BYTEORDER == SDL_BIG_ENDIAN) - { - return p[0] << 16 | p[1] << 8 | p[2]; - } - else - { - return p[0] | p[1] << 8 | p[2] << 16; - } - case 4: - return *(Uint32 *) p; - default: - return 0; // shouldn't happen - } +static void +glib_png_read (png_structp png_ptr, png_bytep out_bytes, png_size_t len) +{ + PngBuffer *buf = (PngBuffer *) png_get_io_ptr (png_ptr); + if (buf->offset + len > buf->size) + png_error (png_ptr, "Read beyond buffer"); + + memcpy (out_bytes, buf->data + buf->offset, len); + buf->offset += len; } World * -World_load_from_file (const char *path) +World_load_from_path (const char *path) { + UNUSED (path); + World *self = g_new0 (World, 1); self->buildings = g_array_new (FALSE, FALSE, sizeof (Building *)); - SDL_Surface *surf = IMG_Load (path); + char *png_content = NULL; + size_t len = 0; + GError *err = NULL; - if (!surf) + if (!g_file_get_contents (path, &png_content, &len, &err)) { - g_warning ("Failed to load map from path %s (returning NULL): %s", path, - SDL_GetError ()); + g_critical ("Failed to load map file %s: %s", path, err->message); + g_error_free (err); return NULL; } - self->width = surf->w; - self->height = surf->h; - self->tiles = - g_array_sized_new (FALSE, FALSE, sizeof (MapTile *), surf->w * surf->h); + png_structp png_ptr = + png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + png_infop info_ptr = png_create_info_struct (png_ptr); - SDL_LockSurface (surf); - const SDL_PixelFormatDetails *details = - SDL_GetPixelFormatDetails (surf->format); - - for (int y = 0; y < surf->h; ++y) + if (!png_ptr || !info_ptr) { - for (int x = 0; x < surf->w; ++x) + g_critical ("%s is not a valid PNG file.", path); + g_free (png_content); + return NULL; + } + + if (setjmp (png_jmpbuf (png_ptr))) + { + g_critical ("Something got fucked up while loading PNG."); + png_destroy_read_struct (&png_ptr, &info_ptr, NULL); + g_free (png_content); + return NULL; + } + + PngBuffer buf = { (unsigned char *) png_content, len, 0 }; + png_set_read_fn (png_ptr, &buf, glib_png_read); + + png_read_info (png_ptr, info_ptr); + + self->width = png_get_image_width (png_ptr, info_ptr); + self->height = png_get_image_height (png_ptr, info_ptr); + self->tiles = g_array_sized_new (FALSE, FALSE, sizeof (MapTile *), + self->width * self->height); + + png_byte color_type = png_get_color_type (png_ptr, info_ptr); + png_byte bit_depth = png_get_bit_depth (png_ptr, info_ptr); + + if (bit_depth == 16) + png_set_strip_16 (png_ptr); + if (color_type == PNG_COLOR_TYPE_PALETTE) + png_set_palette_to_rgb (png_ptr); + if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) + png_set_expand_gray_1_2_4_to_8 (png_ptr); + if (png_get_valid (png_ptr, info_ptr, PNG_INFO_tRNS)) + png_set_tRNS_to_alpha (png_ptr); + if (color_type == PNG_COLOR_TYPE_RGB || + color_type == PNG_COLOR_TYPE_GRAY || + color_type == PNG_COLOR_TYPE_PALETTE) + png_set_filler (png_ptr, 0xFF, PNG_FILLER_AFTER); + if (color_type == PNG_COLOR_TYPE_GRAY || + color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + png_set_gray_to_rgb (png_ptr); + + png_read_update_info (png_ptr, info_ptr); + + int rowbytes = png_get_rowbytes (png_ptr, info_ptr); + unsigned char *image_data = g_malloc (rowbytes * self->height); + png_bytep *row_pointers = g_new (png_bytep, self->height); + + for (unsigned int y = 0; y < self->height; y++) + row_pointers[y] = image_data + y * rowbytes; + + png_read_image (png_ptr, row_pointers); + + for (unsigned int y = 0; y < self->height; ++y) + { + for (unsigned int x = 0; x < self->width; ++x) { MapTile *mt = g_new0 (MapTile, 1); mt->x = x; mt->y = y; - Uint32 pixel = _surf_pixel (surf, x, y); - Uint8 r, g, b, a; - /* NULL is a palette, only used in indexed images. */ - SDL_GetRGBA (pixel, details, NULL, &r, &g, &b, &a); + unsigned char *px = &row_pointers[y][x * 4]; + unsigned char r = px[0]; + unsigned char g = px[1]; + unsigned char b = px[2]; /* Improved color ranges (handles light blue water better) */ if (b > 120 && (b > g || b > r)) @@ -82,13 +126,15 @@ World_load_from_file (const char *path) mt->terrain_type = TERRAIN_HILL; } /* Thanks, ChatGPT! */ - + g_array_append_val (self->tiles, mt); } } - SDL_UnlockSurface (surf); - SDL_DestroySurface (surf); + png_destroy_read_struct (&png_ptr, &info_ptr, NULL); + g_free (row_pointers); + g_free (image_data); + g_free (png_content); return self; } diff --git a/src/common/world.h b/src/common/world.h index a7c4a08..feaab1d 100644 --- a/src/common/world.h +++ b/src/common/world.h @@ -1,9 +1,9 @@ #pragma once -#include -#include - #include +#include + +#include typedef enum TerrainType { @@ -37,5 +37,5 @@ typedef struct Building unsigned int (*at_what_cost) (struct Building * self, World * world); } Building; -World *World_load_from_file (const char *path); +World *World_load_from_path (const char *path); void World_tick (World * self);