changeset 1569:0ec89dadb36d

Add code for loading PNG images. Added 360 controller image. WIP work on gamepad mapping UI
author Michael Pavone <pavone@retrodev.com>
date Thu, 19 Apr 2018 00:51:10 -0700
parents d14490dee01f
children bc96bb3a0998
files images/360.png nuklear_ui/blastem_nuklear.c png.c png.h
diffstat 4 files changed, 336 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
Binary file images/360.png has changed
--- a/nuklear_ui/blastem_nuklear.c	Tue Apr 17 23:05:08 2018 -0700
+++ b/nuklear_ui/blastem_nuklear.c	Thu Apr 19 00:51:10 2018 -0700
@@ -12,6 +12,7 @@
 #include "../blastem.h"
 #include "../config.h"
 #include "../io.h"
+#include "../png.h"
 
 static struct nk_context *context;
 
@@ -495,9 +496,12 @@
 		nk_end(context);
 	}
 }
+static struct nk_image controller_image;
 void view_controllers(struct nk_context *context)
 {
 	if (nk_begin(context, "Controller Bindings", nk_rect(0, 0, render_width(), render_height()), 0)) {
+		nk_layout_row_static(context, render_height() - 50, render_width() - 80, 1);
+		nk_image(context, controller_image);
 		nk_layout_row_static(context, 34, (render_width() - 80) / 2, 1);
 		if (nk_button_label(context, "Back")) {
 			pop_view();
@@ -1013,6 +1017,28 @@
 	render_set_ui_render_fun(blastem_nuklear_render);
 	render_set_event_handler(handle_event);
 	render_set_gl_context_handlers(context_destroyed, context_created);
+	
+	FILE *f = fopen("images/360.png", "rb");
+	long buf_size = file_size(f);
+	uint8_t *buf = malloc(buf_size);
+	if (buf_size == fread(buf, 1, buf_size, f)) {
+		uint32_t width, height;
+		uint32_t *pixels = load_png(buf, buf_size, &width, &height);
+		if (pixels) {
+			GLuint tex;
+			glGenTextures(1, &tex);
+			glBindTexture(GL_TEXTURE_2D, tex);
+			glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+			glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+			glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+			glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+			glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, pixels);
+			free(pixels);
+			controller_image = nk_image_id((int)tex);
+		}
+	}
+	free(buf);
+	
 	active = 1;
 	ui_idle_loop();
 }
--- a/png.c	Tue Apr 17 23:05:08 2018 -0700
+++ b/png.c	Thu Apr 19 00:51:10 2018 -0700
@@ -1,6 +1,7 @@
 #include <stdint.h>
 #include <stdlib.h>
 #include <stdio.h>
+#include <string.h>
 #include "zlib/zlib.h"
 
 static const char png_magic[] = {0x89, 'P', 'N', 'G', '\r', '\n', 0x1A, '\n'};
@@ -10,8 +11,11 @@
 static const char iend[] = {'I', 'E', 'N', 'D'};
 
 enum {
+	COLOR_GRAY,
 	COLOR_TRUE = 2,
-	COLOR_INDEXED
+	COLOR_INDEXED,
+	COLOR_GRAY_ALPHA,
+	COLOR_TRUE_ALPHA=6
 };
 
 static void write_chunk(FILE *f, const char*id, uint8_t *buffer, uint32_t size)
@@ -136,3 +140,307 @@
 	write_chunk(f, iend, NULL, 0);
 	free(compressed);
 }
+
+typedef uint8_t (*filter_fun)(uint8_t *cur, uint8_t *last, uint8_t bpp, uint32_t x);
+typedef uint32_t (*pixel_fun)(uint8_t **cur, uint8_t **last, uint8_t bpp, uint32_t x, filter_fun);
+
+static uint8_t filter_none(uint8_t *cur, uint8_t *last, uint8_t bpp, uint32_t x)
+{
+	return *cur;
+}
+
+static uint8_t filter_sub(uint8_t *cur, uint8_t *last, uint8_t bpp, uint32_t x)
+{
+	if (x) {
+		return *cur + *(cur - bpp);
+	} else {
+		return *cur;
+	}
+}
+
+static uint8_t filter_up(uint8_t *cur, uint8_t *last, uint8_t bpp, uint32_t x)
+{
+	if (last) {
+		return *cur + *last;
+	} else {
+		return *cur;
+	}
+}
+
+static uint8_t filter_avg(uint8_t *cur, uint8_t *last, uint8_t bpp, uint32_t x)
+{
+	uint8_t prev = x ? *(cur - bpp) : 0;
+	uint8_t prior = last ? *last : 0;
+	return *cur + ((prev + prior) >> 1);
+}
+
+static uint8_t paeth(uint8_t a, uint8_t b, uint8_t c)
+{
+	int32_t p = a + b - c;
+	int32_t pa = abs(p - a);
+	int32_t pb = abs(p - b);
+	int32_t pc = abs(p - c);
+	if (pa <= pb && pa <= pc) {
+		return a;
+	}
+	if (pb <= pc) {
+		return b;
+	}
+	return c;
+}
+
+static uint8_t filter_paeth(uint8_t *cur, uint8_t *last, uint8_t bpp, uint32_t x)
+{
+	uint8_t prev, prev_prior;
+	if (x) {
+		prev = *(cur - bpp);
+		prev_prior = *(last - bpp);
+	} else {
+		prev = prev_prior = 0;
+	}
+	uint8_t prior = last ? *last : 0;
+	return *cur + paeth(prev, prior, prev_prior);
+}
+
+static uint32_t pixel_gray(uint8_t **cur, uint8_t **last, uint8_t bpp, uint32_t x, filter_fun filter)
+{
+	uint8_t value = filter(*cur, *last, bpp, x);
+	(*cur)++;
+	if (*last) {
+		(*last)++;
+	}
+	return 0xFF000000 | value << 16 | value << 8 | value;
+}
+
+static uint32_t pixel_true(uint8_t **cur, uint8_t **last, uint8_t bpp, uint32_t x, filter_fun filter)
+{
+	uint8_t red = filter(*cur, *last, bpp, x);
+	(*cur)++;
+	if (*last) {
+		(*last)++;
+	}
+	uint8_t green = filter(*cur, *last, bpp, x);
+	(*cur)++;
+	if (*last) {
+		(*last)++;
+	}
+	uint8_t blue = filter(*cur, *last, bpp, x);
+	(*cur)++;
+	if (*last) {
+		(*last)++;
+	}
+	return 0xFF000000 | red << 16 | green << 8 | blue;
+}
+
+static uint32_t pixel_gray_alpha(uint8_t **cur, uint8_t **last, uint8_t bpp, uint32_t x, filter_fun filter)
+{
+	uint8_t value = filter(*cur, *last, bpp, x);
+	(*cur)++;
+	if (*last) {
+		(*last)++;
+	}
+	uint8_t alpha = filter(*cur, *last, bpp, x);
+	(*cur)++;
+	if (*last) {
+		(*last)++;
+	}
+	return alpha << 24 | value << 16 | value << 8 | value;
+}
+
+static uint32_t pixel_true_alpha(uint8_t **cur, uint8_t **last, uint8_t bpp, uint32_t x, filter_fun filter)
+{
+	uint8_t red = filter(*cur, *last, bpp, x);
+	(*cur)++;
+	if (*last) {
+		(*last)++;
+	}
+	uint8_t green = filter(*cur, *last, bpp, x);
+	(*cur)++;
+	if (*last) {
+		(*last)++;
+	}
+	uint8_t blue = filter(*cur, *last, bpp, x);
+	(*cur)++;
+	if (*last) {
+		(*last)++;
+	}
+	uint8_t alpha = filter(*cur, *last, bpp, x);
+	(*cur)++;
+	if (*last) {
+		(*last)++;
+	}
+	return alpha << 24 | red << 16 | green << 8 | blue;
+}
+
+static filter_fun filters[] = {filter_none, filter_sub, filter_up, filter_avg, filter_paeth};
+
+#define MIN_CHUNK_SIZE 12
+#define MIN_IHDR_SIZE 0xD
+#define MAX_SUPPORTED_DIM 32767 //chosen to avoid possibility of overflow when calculating uncompressed size
+uint32_t *load_png(uint8_t *buffer, uint32_t buf_size, uint32_t *width, uint32_t *height)
+{
+	if (buf_size < sizeof(png_magic) || memcmp(buffer, png_magic, sizeof(png_magic))) {
+		return NULL;
+	}
+	uint32_t cur = sizeof(png_magic);
+	uint8_t has_header = 0;
+	uint8_t bits, color_type, comp_type, filter_type, interlace;
+	uint8_t *idat_buf = NULL;
+	uint8_t idat_needs_free = 0;
+	uint32_t idat_size;
+	uint32_t *out = NULL;
+	uint32_t *palette = NULL;
+	while(cur + MIN_CHUNK_SIZE <= buf_size)
+	{
+		uint32_t chunk_size = buffer[cur++] << 24;
+		chunk_size |=  buffer[cur++] << 16;
+		chunk_size |=  buffer[cur++] << 8;
+		chunk_size |=  buffer[cur++];
+		if (!memcmp(ihdr, buffer + cur, sizeof(ihdr))) {
+			if (chunk_size < MIN_IHDR_SIZE || cur + MIN_IHDR_SIZE > buf_size) {
+				return NULL;
+			}
+			cur += sizeof(ihdr);
+			*width = buffer[cur++] << 24;
+			*width |=  buffer[cur++] << 16;
+			*width |=  buffer[cur++] << 8;
+			*width |=  buffer[cur++];
+			*height = buffer[cur++] << 24;
+			*height |=  buffer[cur++] << 16;
+			*height |=  buffer[cur++] << 8;
+			*height |=  buffer[cur++];
+			if (*width > MAX_SUPPORTED_DIM || *height > MAX_SUPPORTED_DIM) {
+				return NULL;
+			}
+			bits = buffer[cur++];
+			if (bits != 8) {
+				//only support 8-bits per element for now
+				return NULL;
+			}
+			color_type = buffer[cur++];
+			if (color_type > COLOR_TRUE_ALPHA || color_type == 1 || color_type == 5) {
+				//reject invalid color type
+				return NULL;
+			}
+			comp_type = buffer[cur++];
+			if (comp_type) {
+				//only compression type 0 is defined by the spec
+				return NULL;
+			}
+			filter_type = buffer[cur++];
+			interlace = buffer[cur++];
+			if (interlace) {
+				//interlacing not supported for now
+				return NULL;
+			}
+			cur += chunk_size - MIN_IHDR_SIZE;
+			has_header = 1;
+		} else {
+			if (!has_header) {
+				//IHDR is required to be the first chunk, fail if it isn't
+				break;
+			}
+			if (!memcmp(plte, buffer + cur, sizeof(plte))) {
+				//TODO: implement paletted images
+			} else if (!memcmp(idat, buffer + cur, sizeof(idat))) {
+				cur += sizeof(idat);
+				if (idat_buf) {
+					if (idat_needs_free) {
+						idat_buf = realloc(idat_buf, idat_size + chunk_size);
+					} else {
+						uint8_t *tmp = idat_buf;
+						idat_buf = malloc(idat_size + chunk_size);
+						memcpy(idat_buf, tmp, idat_size);
+					}
+					memcpy(idat_buf + idat_size, buffer + cur, chunk_size);
+					idat_size += chunk_size;
+				} else {
+					idat_buf = buffer + cur;
+					idat_size = chunk_size;
+				}
+				cur += chunk_size;
+			} else if (!memcmp(iend, buffer + cur, sizeof(iend))) {
+				if (!idat_buf) {
+					break;
+				}
+				if (!palette && color_type == COLOR_INDEXED) {
+					//indexed color, but no PLTE chunk found
+					return NULL;
+				}
+				uLongf uncompressed_size = *width * *height;
+				uint8_t bpp;
+				pixel_fun pixel;
+				switch (color_type)
+				{
+				case COLOR_GRAY:
+					uncompressed_size *= bits / 8;
+					bpp = bits/8;
+					pixel = pixel_gray;
+					break;
+				case COLOR_TRUE:
+					uncompressed_size *= 3 * bits / 8;
+					bpp = 3 * bits/8;
+					pixel = pixel_true;
+					break;
+				case COLOR_INDEXED: {
+					uint32_t pixels_per_byte = 8 / bits;
+					uncompressed_size = (*width / pixels_per_byte) * *height;
+					if (*width % pixels_per_byte) {
+						uncompressed_size += *height;
+					}
+					bpp = 1;
+					break;
+				}
+				case COLOR_GRAY_ALPHA:
+					uncompressed_size *= bits / 4;
+					bpp = bits / 4;
+					pixel = pixel_gray_alpha;
+					break;
+				case COLOR_TRUE_ALPHA:
+					uncompressed_size *= bits / 2;
+					bpp = bits / 2;
+					pixel = pixel_true_alpha;
+					break;
+				}
+				//add filter type byte
+				uncompressed_size += *height;
+				uint8_t *decomp_buffer = malloc(uncompressed_size);
+				if (Z_OK != uncompress(decomp_buffer, &uncompressed_size, idat_buf, idat_size)) {
+					free(decomp_buffer);
+					break;
+				}
+				out = calloc(*width * *height, sizeof(uint32_t));
+				uint32_t *cur_pixel = out;
+				uint8_t *cur_byte = decomp_buffer;
+				uint8_t *last_line = NULL;
+				for (uint32_t y = 0; y < *height; y++)
+				{
+					uint8_t filter_type = *(cur_byte++);
+					if (filter_type >= sizeof(filters)/sizeof(*filters)) {
+						free(out);
+						out = NULL;
+						free(decomp_buffer);
+						break;
+					}
+					filter_fun filter = filters[filter_type];
+					uint8_t *line_start = cur_byte;
+					for (uint32_t x = 0; x < *width; x++)
+					{
+						*(cur_pixel++) = pixel(&cur_byte, &last_line, bpp, x, filter);
+					}
+					last_line = line_start;
+				}
+			} else {
+				//skip uncrecognized chunks
+				cur += 4 + chunk_size;
+			}
+		}
+		//skip CRC for now
+		cur += sizeof(uint32_t);
+	}
+	if (idat_needs_free) {
+		free(idat_buf);
+	}
+	free(palette);
+	return out;
+}
--- a/png.h	Tue Apr 17 23:05:08 2018 -0700
+++ b/png.h	Thu Apr 19 00:51:10 2018 -0700
@@ -3,5 +3,6 @@
 
 void save_png24(FILE *f, uint32_t *buffer, uint32_t width, uint32_t height, uint32_t pitch);
 void save_png(FILE *f, uint32_t *buffer, uint32_t width, uint32_t height, uint32_t pitch);
+uint32_t *load_png(uint8_t *buffer, uint32_t buf_size, uint32_t *width, uint32_t *height);
 
 #endif //PNG_H_