#include typedef struct { const unsigned char *data; size_t size; size_t offset; } PngBuffer; 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_path (const char *path) { World *self = g_new0 (World, 1); self->buildings = g_array_new (FALSE, FALSE, sizeof (Building *)); char *png_content = NULL; size_t len = 0; GError *err = NULL; if (!g_file_get_contents (path, &png_content, &len, &err)) { g_critical ("Failed to load map file %s: %s", path, err->message); g_error_free (err); return NULL; } 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); if (!png_ptr || !info_ptr) { 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_set_expand (png_ptr); png_set_gray_to_rgb (png_ptr); png_set_filler (png_ptr, 0xFF, PNG_FILLER_AFTER); if (png_get_bit_depth (png_ptr, info_ptr) == 16) { png_set_strip_16 (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; unsigned char *px = &row_pointers[y][x * 4]; unsigned char r = px[0], g = px[1], b = px[2]; /* Improved color ranges (handles light blue water better) */ if (b > 120 && (b > g || b > r)) { /* Water: medium-to-light blues */ mt->terrain_type = TERRAIN_WATER; } else if (g > 140 && g >= r && g >= b) { /* Green-dominant = flat/grassland */ mt->terrain_type = TERRAIN_FLAT; } else { /* Otherwise: hills/mountains */ mt->terrain_type = TERRAIN_HILL; } /* Thanks, ChatGPT! */ g_array_append_val (self->tiles, mt); } } png_destroy_read_struct (&png_ptr, &info_ptr, NULL); g_free (row_pointers); g_free (image_data); g_free (png_content); return self; }