diff --git a/Makefile b/Makefile index 5994b783a5..b7d63eef99 100644 --- a/Makefile +++ b/Makefile @@ -108,6 +108,12 @@ ifeq ($(HAVE_FREETYPE), 1) DEFINES += $(FREETYPE_CFLAGS) endif +ifeq ($(HAVE_IMLIB), 1) + OBJ += gfx/image.o + LIBS += $(IMLIB_LIBS) + DEFINES += $(IMLIB_CFLAGS) +endif + ifeq ($(HAVE_FFMPEG), 1) OBJ += record/ffemu.o LIBS += $(AVCODEC_LIBS) $(AVCORE_LIBS) $(AVFORMAT_LIBS) $(AVUTIL_LIBS) $(SWSCALE_LIBS) diff --git a/gfx/image.c b/gfx/image.c new file mode 100644 index 0000000000..500928f1b4 --- /dev/null +++ b/gfx/image.c @@ -0,0 +1,53 @@ +/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes. + * Copyright (C) 2010-2011 - Hans-Kristian Arntzen + * + * Some code herein may be based on code found in BSNES. + * + * SSNES is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * SSNES is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with SSNES. + * If not, see . + */ + + +#include "image.h" +#include +#include +#include +#include + +// Imlib2 <3<3<3<3 +bool texture_image_load(const char *path, struct texture_image *out_img) +{ + Imlib_Image img; + img = imlib_load_image(path); + if (!img) + return false; + + imlib_context_set_image(img); + + out_img->width = imlib_image_get_width(); + out_img->height = imlib_image_get_height(); + + size_t size = out_img->width * out_img->height * sizeof(uint32_t); + out_img->pixels = malloc(size); + if (!out_img->pixels) + { + imlib_free_image(); + return false; + } + + const uint32_t *read = imlib_image_get_data_for_reading_only(); + // Convert ARGB -> RGBA. + for (unsigned i = 0; i < size / sizeof(uint32_t); i++) + out_img->pixels[i] = (read[i] >> 24) | (read[i] << 8); + + imlib_free_image(); + return true; +} diff --git a/gfx/image.h b/gfx/image.h new file mode 100644 index 0000000000..e2644f65bf --- /dev/null +++ b/gfx/image.h @@ -0,0 +1,34 @@ +/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes. + * Copyright (C) 2010-2011 - Hans-Kristian Arntzen + * + * Some code herein may be based on code found in BSNES. + * + * SSNES is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * SSNES is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with SSNES. + * If not, see . + */ + +#ifndef __SSNES_IMAGE_H +#define __SSNES_IMAGE_H + +#include +#include + +struct texture_image +{ + unsigned width; + unsigned height; + uint32_t *pixels; +}; + +bool texture_image_load(const char *path, struct texture_image* img); + + +#endif diff --git a/gfx/shader_glsl.c b/gfx/shader_glsl.c index 2576d4435e..89ee434f86 100644 --- a/gfx/shader_glsl.c +++ b/gfx/shader_glsl.c @@ -19,6 +19,11 @@ #include #include "general.h" #include "shader_glsl.h" +#include "strl.h" + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif #ifdef __APPLE__ #include @@ -38,6 +43,11 @@ #include "gl_common.h" +#ifdef HAVE_IMLIB +#include "image.h" +#endif + + #ifdef __APPLE__ #define pglCreateProgram glCreateProgram #define pglUseProgram glUseProgram @@ -81,6 +91,7 @@ static PFNGLGETATTACHEDSHADERSPROC pglGetAttachedShaders = NULL; #endif #define MAX_PROGRAMS 16 +#define MAX_TEXTURES 4 enum filter_type { @@ -96,6 +107,10 @@ static struct gl_fbo_scale gl_scale[MAX_PROGRAMS]; static unsigned gl_num_programs = 0; static unsigned active_index = 0; +static GLuint gl_teximage[MAX_TEXTURES]; +static unsigned gl_teximage_cnt = 0; +static char gl_teximage_uniforms[MAX_TEXTURES][64]; + struct shader_program { char *vertex; @@ -273,6 +288,86 @@ static bool get_xml_attrs(struct shader_program *prog, xmlNodePtr ptr) return true; } +#ifdef HAVE_IMLIB +static bool get_texture_image(const char *shader_path, xmlNodePtr ptr) +{ + bool linear = true; + xmlChar *filename = xmlGetProp(ptr, (const xmlChar*)"file"); + xmlChar *filter = xmlGetProp(ptr, (const xmlChar*)"filter"); + xmlChar *id = xmlGetProp(ptr, (const xmlChar*)"id"); + + if (!id) + { + SSNES_ERR("Could not find ID in texture.\n"); + goto error; + } + + if (!filename) + { + SSNES_ERR("Could not find filename in texture.\n"); + goto error; + } + + if (filter && strcmp((const char*)filter, "nearest") == 0) + linear = false; + + char tex_path[256]; + strlcpy(tex_path, shader_path, sizeof(tex_path)); + + char *last = strrchr(tex_path, '/'); + if (!last) last = strrchr(tex_path, '\\'); + if (last) last[1] = '\0'; + + strlcat(tex_path, (const char*)filename, sizeof(tex_path)); + + struct texture_image img; + SSNES_LOG("Loading texture image from: \"%s\" ...\n", tex_path); + if (!texture_image_load(tex_path, &img)) + { + SSNES_ERR("Failed to load texture image from: \"%s\"\n", tex_path); + goto error; + } + + strlcpy(gl_teximage_uniforms[gl_teximage_cnt], (const char*)id, sizeof(gl_teximage_uniforms[0])); + + glGenTextures(1, &gl_teximage[gl_teximage_cnt]); + glActiveTexture(GL_TEXTURE0 + gl_teximage_cnt + 1); + glBindTexture(GL_TEXTURE_2D, gl_teximage[gl_teximage_cnt]); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, linear ? GL_LINEAR : GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, linear ? GL_LINEAR : GL_NEAREST); + + glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + glPixelStorei(GL_UNPACK_ROW_LENGTH, img.width); + glTexImage2D(GL_TEXTURE_2D, + 0, GL_RGBA, img.width, img.height, 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, img.pixels); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, 0); + free(img.pixels); + + xmlFree(filename); + xmlFree(id); + if (filter) + xmlFree(filter); + + gl_teximage_cnt++; + + return true; + +error: + if (filename) + xmlFree(filename); + if (filter) + xmlFree(filter); + if (filter) + xmlFree(id); + return false; +} +#endif + static unsigned get_xml_shaders(const char *path, struct shader_program *prog, size_t size) { LIBXML_TEST_VERSION; @@ -354,6 +449,16 @@ static unsigned get_xml_shaders(const char *path, struct shader_program *prog, s } num++; } +#ifdef HAVE_IMLIB + else if (strcmp((const char*)cur->name, "texture") == 0) + { + if (!get_texture_image(path, cur)) + { + SSNES_ERR("Texture image failed to load.\n"); + goto error; + } + } +#endif } if (num == 0) @@ -587,6 +692,10 @@ void gl_glsl_deinit(void) pglDeleteProgram(gl_program[i]); } + + glDeleteTextures(gl_teximage_cnt, gl_teximage); + gl_teximage_cnt = 0; + memset(gl_teximage_uniforms, 0, sizeof(gl_teximage_uniforms)); } memset(gl_program, 0, sizeof(gl_program)); @@ -617,6 +726,12 @@ void gl_glsl_set_params(unsigned width, unsigned height, location = pglGetUniformLocation(gl_program[active_index], "rubyFrameCount"); pglUniform1i(location, frame_count); + + for (unsigned i = 0; i < gl_teximage_cnt; i++) + { + location = pglGetUniformLocation(gl_program[active_index], gl_teximage_uniforms[i]); + pglUniform1i(location, i + 1); + } } } diff --git a/qb/config.libs.sh b/qb/config.libs.sh index 0ddc03939e..a765ed6521 100644 --- a/qb/config.libs.sh +++ b/qb/config.libs.sh @@ -43,6 +43,7 @@ check_critical SDL "Cannot find SDL library." check_lib CG -lCg cgCreateContext check_pkgconf XML libxml-2.0 +check_pkgconf IMLIB imlib2 if [ $HAVE_FFMPEG != no ]; then check_pkgconf AVCODEC libavcodec @@ -64,7 +65,7 @@ check_lib XVIDEO -lXv XvShmCreateImage check_lib STRL -lc strlcpy # Creates config.mk and config.h. -VARS="ALSA OSS AL RSOUND ROAR JACK PULSE SDL DYLIB CG XML DYNAMIC FFMPEG AVCODEC AVFORMAT AVCORE AVUTIL SWSCALE SRC CONFIGFILE FREETYPE XVIDEO NETPLAY FBO STRL" +VARS="ALSA OSS AL RSOUND ROAR JACK PULSE SDL DYLIB CG XML IMLIB DYNAMIC FFMPEG AVCODEC AVFORMAT AVCORE AVUTIL SWSCALE SRC CONFIGFILE FREETYPE XVIDEO NETPLAY FBO STRL" create_config_make config.mk $VARS create_config_header config.h $VARS diff --git a/qb/config.params.sh b/qb/config.params.sh index 5b8a8706e6..93f8ee7106 100644 --- a/qb/config.params.sh +++ b/qb/config.params.sh @@ -26,3 +26,4 @@ add_command_line_enable JACK "Enable JACK support" auto add_command_line_enable PULSE "Enable PulseAudio support" auto add_command_line_enable FREETYPE "Enable FreeType support" auto add_command_line_enable XVIDEO "Enable XVideo support" auto +add_command_line_enable IMLIB "Enable imlib2 support" auto