132 lines
3.2 KiB
C
132 lines
3.2 KiB
C
#include <common/world.h>
|
|
|
|
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;
|
|
}
|