From 9da5a83550777756ffb9041ebf1a2a40f1ed4e46 Mon Sep 17 00:00:00 2001 From: Brandon Wright Date: Thu, 10 May 2018 18:47:55 -0500 Subject: [PATCH] Rudimentary GLSL Shader support. --- gtk/Makefile.am | 8 +- gtk/src/gtk_display_driver_opengl.cpp | 47 +- gtk/src/gtk_display_driver_opengl.h | 6 +- gtk/src/{Cg => shaders}/CCGShader.cpp | 0 gtk/src/{Cg => shaders}/CCGShader.h | 0 gtk/src/{Cg => shaders}/CGLCG.cpp | 199 +----- gtk/src/{Cg => shaders}/CGLCG.h | 34 - gtk/src/{Cg => shaders}/cgFunctions.cpp | 0 gtk/src/{Cg => shaders}/cgFunctions.h | 0 gtk/src/{Cg => shaders}/cgMini.h | 0 gtk/src/shaders/glsl.cpp | 787 ++++++++++++++++++++++++ gtk/src/shaders/glsl.h | 118 ++++ gtk/src/shaders/shader_helpers.cpp | 223 +++++++ gtk/src/shaders/shader_helpers.h | 44 ++ 14 files changed, 1230 insertions(+), 236 deletions(-) rename gtk/src/{Cg => shaders}/CCGShader.cpp (100%) rename gtk/src/{Cg => shaders}/CCGShader.h (100%) rename gtk/src/{Cg => shaders}/CGLCG.cpp (84%) rename gtk/src/{Cg => shaders}/CGLCG.h (92%) rename gtk/src/{Cg => shaders}/cgFunctions.cpp (100%) rename gtk/src/{Cg => shaders}/cgFunctions.h (100%) rename gtk/src/{Cg => shaders}/cgMini.h (100%) create mode 100644 gtk/src/shaders/glsl.cpp create mode 100644 gtk/src/shaders/glsl.h create mode 100644 gtk/src/shaders/shader_helpers.cpp create mode 100644 gtk/src/shaders/shader_helpers.h diff --git a/gtk/Makefile.am b/gtk/Makefile.am index 22b46287..92ec77c0 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -235,9 +235,11 @@ if OPENGL snes9x_gtk_SOURCES += \ src/gtk_display_driver_opengl.cpp \ src/gtk_display_driver_opengl.h \ - src/Cg/CGLCG.cpp \ - src/Cg/cgFunctions.cpp \ - src/Cg/CCGShader.cpp + src/shaders/CGLCG.cpp \ + src/shaders/cgFunctions.cpp \ + src/shaders/CCGShader.cpp \ + src/shaders/glsl.cpp \ + src/shaders/shader_helpers.cpp endif if XV diff --git a/gtk/src/gtk_display_driver_opengl.cpp b/gtk/src/gtk_display_driver_opengl.cpp index 29517b74..1f8e2f12 100644 --- a/gtk/src/gtk_display_driver_opengl.cpp +++ b/gtk/src/gtk_display_driver_opengl.cpp @@ -11,7 +11,7 @@ #include "gtk_display.h" #include "gtk_display_driver_opengl.h" -#include "Cg/CGLCG.h" +#include "shaders/CGLCG.h" S9xOpenGLDisplayDriver::S9xOpenGLDisplayDriver (Snes9xWindow *window, Snes9xConfig *config) @@ -48,7 +48,7 @@ S9xOpenGLDisplayDriver::update (int width, int height, int yoffset) #endif - if (using_cg_shaders) + if (using_cg_shaders || using_glsl_shaders) { glBindTexture (tex_target, texmap); } @@ -233,7 +233,12 @@ S9xOpenGLDisplayDriver::update (int width, int height, int yoffset) texcoords[4] = texcoords[2]; } - if (using_shaders && using_cg_shaders) + if (using_shaders && using_glsl_shaders) + { + glsl_shader->render (texmap, width, height, w, h); + glViewport (x, allocation.height - y - h, w, h); + } + else if (using_shaders && using_cg_shaders) { xySize texture_size, input_size, viewport_size; texture_size.x = texture_width; @@ -376,8 +381,30 @@ S9xOpenGLDisplayDriver::load_shaders (const char *shader_file) xmlDoc *xml_doc = NULL; xmlNodePtr node = NULL; char *fragment = NULL, *vertex = NULL; + const char *extensions = (const char *) glGetString (GL_EXTENSIONS); int length = strlen (shader_file); + + if ((length > 6 && !strcasecmp(shader_file + length - 6, ".glslp")) || + (length > 5 && !strcasecmp(shader_file + length - 5, ".glsl"))) + { + if (shaders_available() && strstr(extensions, "non_power_of_two")) + { + glsl_shader = new GLSLShader; + if (glsl_shader->load_shader ((char *) shader_file)) + { + using_glsl_shaders = 1; + return 1; + } + delete glsl_shader; + return 0; + } + else + { + printf ("Need shader extensions and non-power-of-two-textures for GLSL.\n"); + } + } + if ((length > 4 && !strcasecmp(shader_file + length - 4, ".cgp")) || (length > 3 && !strcasecmp(shader_file + length - 3, ".cg"))) { @@ -480,6 +507,8 @@ S9xOpenGLDisplayDriver::load_shaders (const char *shader_file) int S9xOpenGLDisplayDriver::opengl_defaults (void) { + const char *extensions = (const char *) glGetString (GL_EXTENSIONS); + using_pbos = 0; if (config->use_pbos) { @@ -497,8 +526,11 @@ S9xOpenGLDisplayDriver::opengl_defaults (void) using_shaders = 0; using_cg_shaders = 0; + using_glsl_shaders = 0; cg_context = NULL; cg_shader = NULL; + glsl_shader = NULL; + if (config->use_shaders) { if (!load_shaders (config->fragment_shader)) @@ -516,8 +548,6 @@ S9xOpenGLDisplayDriver::opengl_defaults (void) texture_height = 1024; dyn_resizing = FALSE; - const char *extensions = (const char *) glGetString (GL_EXTENSIONS); - if (extensions && config->npot_textures) { if (!using_shaders && strstr (extensions, "_texture_rectangle")) @@ -800,7 +830,12 @@ S9xOpenGLDisplayDriver::deinit (void) if (!initialized) return; - if (using_shaders && using_cg_shaders) + if (using_shaders && using_glsl_shaders) + { + glsl_shader->destroy(); + delete glsl_shader; + } + else if (using_shaders && using_cg_shaders) { delete cg_shader; cgDestroyContext (cg_context); diff --git a/gtk/src/gtk_display_driver_opengl.h b/gtk/src/gtk_display_driver_opengl.h index 49d5383e..b6f38c0f 100644 --- a/gtk/src/gtk_display_driver_opengl.h +++ b/gtk/src/gtk_display_driver_opengl.h @@ -7,7 +7,8 @@ #include #include -#include "Cg/CGLCG.h" +#include "shaders/CGLCG.h" +#include "shaders/glsl.h" #define PBO_FMT_16 0 #define PBO_FMT_24 1 @@ -72,6 +73,9 @@ class S9xOpenGLDisplayDriver : public S9xDisplayDriver CGcontext cg_context; CGLCG *cg_shader; + int using_glsl_shaders; + GLSLShader *glsl_shader; + Display *display; Window xwindow; Colormap xcolormap; diff --git a/gtk/src/Cg/CCGShader.cpp b/gtk/src/shaders/CCGShader.cpp similarity index 100% rename from gtk/src/Cg/CCGShader.cpp rename to gtk/src/shaders/CCGShader.cpp diff --git a/gtk/src/Cg/CCGShader.h b/gtk/src/shaders/CCGShader.h similarity index 100% rename from gtk/src/Cg/CCGShader.h rename to gtk/src/shaders/CCGShader.h diff --git a/gtk/src/Cg/CGLCG.cpp b/gtk/src/shaders/CGLCG.cpp similarity index 84% rename from gtk/src/Cg/CGLCG.cpp rename to gtk/src/shaders/CGLCG.cpp index 85f61d1a..154cd74c 100644 --- a/gtk/src/Cg/CGLCG.cpp +++ b/gtk/src/shaders/CGLCG.cpp @@ -196,6 +196,7 @@ #include #include "../gtk_display_driver_opengl.h" +#include "shader_helpers.h" static char* ReadShaderFileContents(const char *filename) { @@ -541,7 +542,7 @@ bool CGLCG::LoadShader(const char* shaderFile) GL_UNSIGNED_SHORT_5_6_5, NULL); glBindTexture(GL_TEXTURE_2D, 0); - prevPasses[i].textureSize.x = prevPasses[i].textureSize.y = prevPasses[i].textureSize.x = prevPasses[i].textureSize.y = 0; + prevPasses[i].textureSize.x = prevPasses[i].textureSize.y = 0; memset(prevPasses[i].texCoords, 0, sizeof(prevPasses[i].texCoords)); } @@ -721,10 +722,14 @@ void CGLCG::Render(GLuint& origTex, pass.tex = shaderPasses[0].tex; memcpy(pass.texCoords, shaderPasses[1].texcoords, sizeof(pass.texCoords)); prevPasses.push_front(pass); + GLint internal_format; + glBindTexture(GL_TEXTURE_2D, pass.tex); + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, &internal_format); + glBindTexture(GL_TEXTURE_2D, origTex); glTexImage2D(GL_TEXTURE_2D, 0, - GL_RGB, + internal_format, textureSize.x, textureSize.y, 0, @@ -741,10 +746,6 @@ void CGLCG::Render(GLuint& origTex, that will be used in the main ogl code */ - /* RECT - displayRect=CalculateDisplayRect(shaderPasses.back().outputSize.x,shaderPasses.back().outputSize.y,windowSize.x,windowSize.y); - glViewport(displayRect.left,windowSize.y-displayRect.bottom,displayRect.right-displayRect.left,displayRect.bottom-displayRect.top); - */ setTexCoords(shaderPasses.size() - 1, shaderPasses.back().outputSize, shaderPasses.back().textureSize, @@ -923,189 +924,3 @@ void CGLCG::resetAttribParams() cgAttribParams.clear(); } -bool CGLCG::loadPngImage(const char* name, - int& outWidth, - int& outHeight, - bool& outHasAlpha, - GLubyte** outData) -{ - png_structp png_ptr; - png_infop info_ptr; - unsigned int sig_read = 0; - FILE* fp; - - if ((fp = fopen(name, "rb")) == NULL) - return false; - - /* Create and initialize the png_struct - * with the desired error handler - * functions. If you want to use the - * default stderr and longjump method, - * you can supply NULL for the last - * three parameters. We also supply the - * the compiler header file version, so - * that we know if the application - * was compiled with a compatible version - * of the library. REQUIRED - */ - png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); - - if (png_ptr == NULL) { - fclose(fp); - return false; - } - - /* Allocate/initialize the memory - * for image information. REQUIRED. */ - info_ptr = png_create_info_struct(png_ptr); - if (info_ptr == NULL) { - fclose(fp); - png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); - return false; - } - - /* Set error handling if you are - * using the setjmp/longjmp method - * (this is the normal method of - * doing things with libpng). - * REQUIRED unless you set up - * your own error handlers in - * the png_create_read_struct() - * earlier. - */ - if (setjmp(png_jmpbuf(png_ptr))) { - /* Free all of the memory associated - * with the png_ptr and info_ptr */ - png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); - fclose(fp); - /* If we get here, we had a - * problem reading the file */ - return false; - } - - /* Set up the output control if - * you are using standard C streams */ - png_init_io(png_ptr, fp); - - /* If we have already - * read some of the signature */ - png_set_sig_bytes(png_ptr, sig_read); - - /* - * If you have enough memory to read - * in the entire image at once, and - * you need to specify only - * transforms that can be controlled - * with one of the PNG_TRANSFORM_* - * bits (this presently excludes - * dithering, filling, setting - * background, and doing gamma - * adjustment), then you can read the - * entire image (including pixels) - * into the info structure with this - * call - * - * PNG_TRANSFORM_STRIP_16 | - * PNG_TRANSFORM_PACKING forces 8 bit - * PNG_TRANSFORM_EXPAND forces to - * expand a palette into RGB - */ - png_read_png(png_ptr, - info_ptr, - PNG_TRANSFORM_STRIP_16 | PNG_TRANSFORM_PACKING | PNG_TRANSFORM_EXPAND, - (png_voidp)NULL); - - outWidth = png_get_image_width(png_ptr, info_ptr); - outHeight = png_get_image_height(png_ptr, info_ptr); - switch (png_get_color_type(png_ptr, info_ptr)) { - case PNG_COLOR_TYPE_RGBA: - outHasAlpha = true; - break; - case PNG_COLOR_TYPE_RGB: - outHasAlpha = false; - break; - default: - png_destroy_read_struct(&png_ptr, &info_ptr, NULL); - fclose(fp); - return false; - } - unsigned int row_bytes = png_get_rowbytes(png_ptr, info_ptr); - *outData = (unsigned char*)malloc(row_bytes * outHeight); - - png_bytepp row_pointers = png_get_rows(png_ptr, info_ptr); - - for (int i = 0; i < outHeight; i++) { - memcpy(*outData + (row_bytes * i), row_pointers[i], row_bytes); - } - - /* Clean up after the read, - * and free any memory allocated */ - png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); - - /* Close the file */ - fclose(fp); - - /* That's it */ - return true; -} - -bool CGLCG::loadTGA(const char* filename, STGA& tgaFile) -{ - FILE* file; - unsigned char type[4]; - unsigned char info[6]; - - file = fopen(filename, "rb"); - - if (!file) - return false; - - fread(&type, sizeof(char), 3, file); - fseek(file, 12, SEEK_SET); - fread(&info, sizeof(char), 6, file); - - // image type either 2 (color) or 3 (greyscale) - if (type[1] != 0 || (type[2] != 2 && type[2] != 3)) { - fclose(file); - return false; - } - - tgaFile.width = info[0] + info[1] * 256; - tgaFile.height = info[2] + info[3] * 256; - tgaFile.byteCount = info[4] / 8; - - if (tgaFile.byteCount != 3 && tgaFile.byteCount != 4) { - fclose(file); - return false; - } - - long imageSize = tgaFile.width * tgaFile.height * tgaFile.byteCount; - - // allocate memory for image data - unsigned char* tempBuf = new unsigned char[imageSize]; - tgaFile.data = new unsigned char[tgaFile.width * tgaFile.height * 4]; - - // read in image data - fread(tempBuf, sizeof(unsigned char), imageSize, file); - - // swap line order and convert to RBGA - for (int i = 0; i < tgaFile.height; i++) { - unsigned char* source = tempBuf + tgaFile.width * (tgaFile.height - 1 - i) * tgaFile.byteCount; - unsigned char* destination = tgaFile.data + tgaFile.width * i * 4; - for (int j = 0; j < tgaFile.width; j++) { - destination[0] = source[2]; - destination[1] = source[1]; - destination[2] = source[0]; - destination[3] = tgaFile.byteCount == 4 ? source[3] : 0xff; - source += tgaFile.byteCount; - destination += 4; - } - } - delete[] tempBuf; - tgaFile.byteCount = 4; - - // close file - fclose(file); - - return true; -} diff --git a/gtk/src/Cg/CGLCG.h b/gtk/src/shaders/CGLCG.h similarity index 92% rename from gtk/src/Cg/CGLCG.h rename to gtk/src/shaders/CGLCG.h index 3cd45497..cf0c3095 100644 --- a/gtk/src/Cg/CGLCG.h +++ b/gtk/src/shaders/CGLCG.h @@ -209,34 +209,6 @@ typedef struct _xySize class CGLCG { private: - typedef struct _STGA - { - _STGA() - { - data = (unsigned char*)0; - width = 0; - height = 0; - byteCount = 0; - } - - ~_STGA() - { - delete[] data; - data = 0; - } - - void destroy() - { - delete[] data; - data = 0; - } - - int width; - int height; - unsigned char byteCount; - unsigned char* data; - } STGA; - typedef struct _shaderPass { cgScaleParams scaleParams; @@ -293,12 +265,6 @@ private: bool topdown = false); void setShaderVars(int pass); void resetAttribParams(); - bool loadPngImage(const char* name, - int& outWidth, - int& outHeight, - bool& outHasAlpha, - GLubyte** outData); - bool loadTGA(const char* filename, STGA& tgaFile); CGcontext cgContext; unsigned int frameCnt; diff --git a/gtk/src/Cg/cgFunctions.cpp b/gtk/src/shaders/cgFunctions.cpp similarity index 100% rename from gtk/src/Cg/cgFunctions.cpp rename to gtk/src/shaders/cgFunctions.cpp diff --git a/gtk/src/Cg/cgFunctions.h b/gtk/src/shaders/cgFunctions.h similarity index 100% rename from gtk/src/Cg/cgFunctions.h rename to gtk/src/shaders/cgFunctions.h diff --git a/gtk/src/Cg/cgMini.h b/gtk/src/shaders/cgMini.h similarity index 100% rename from gtk/src/Cg/cgMini.h rename to gtk/src/shaders/cgMini.h diff --git a/gtk/src/shaders/glsl.cpp b/gtk/src/shaders/glsl.cpp new file mode 100644 index 00000000..d44b0925 --- /dev/null +++ b/gtk/src/shaders/glsl.cpp @@ -0,0 +1,787 @@ +#include "glsl.h" +#include "../../conffile.h" +#include "shader_helpers.h" +#include "../gtk_s9x.h" + +static const GLfloat lut_coords[8] = { 0, 0, 1, 0, 1, 1, 0, 1 }; +static const GLfloat inv_coords[8] = { 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f }; +static const GLfloat tex_coords[8] = { 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f }; +static const GLfloat mvp_ortho[16] = { 2.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 2.0f, 0.0f, 0.0f, + 0.0f, 0.0f, -1.0f, 0.0f, + -1.0f, -1.0f, 0.0f, 1.0f }; + +static void reduce_to_path(char* filename) +{ + for (int i = strlen(filename); i >= 0; i--) + { + if (filename[i] == '\\' || filename[i] == '/') + { + filename[i] = 0; + break; + } + } +} + +static char* read_file(const char *filename) +{ + FILE* file = NULL; + int size; + char* contents; + + file = fopen(filename, "rb"); + if (!file) + return NULL; + + fseek(file, 0, SEEK_END); + size = ftell(file); + fseek(file, 0, SEEK_SET); + + contents = new char[size + 1]; + fread(contents, size, 1, file); + contents[size] = '\0'; + fclose(file); + + return contents; +} + +static int scale_string_to_enum(const char *string, bool last) +{ + if (!strcasecmp(string, "source")) + { + return GLSL_SOURCE; + } + else if (!strcasecmp(string, "viewport")) + { + return GLSL_VIEWPORT; + } + else if (!strcasecmp(string, "absolute")) + { + return GLSL_ABSOLUTE; + } + else if (last) + return GLSL_VIEWPORT; + else + return GLSL_SOURCE; +} + +bool GLSLShader::load_shader_file (char *filename) +{ + ConfigFile conf; + char key[256]; + + if (strlen(filename) < 6 || strcasecmp(&filename[strlen(filename) - 6], ".glslp")) + { + GLSLPass pass; + pass.scale_type_x = pass.scale_type_y = GLSL_NONE; + pass.filter = GLSL_UNDEFINED; + strcpy(pass.filename, filename); + } + else + { + conf.LoadFile(filename); + } + + int shader_count = conf.GetInt("::shaders", 0); + + if (shader_count < 1) + return false; + + this->pass.push_back (GLSLPass()); + + for (int i = 0; i < shader_count; i++) + { + GLSLPass pass; + + snprintf(key, 256, "::filter_linear%u", i); + pass.filter = conf.Exists(key) ? conf.GetBool(key) ? GL_LINEAR : GL_NEAREST : GLSL_UNDEFINED; + + sprintf(key, "::scale_type%u", i); + const char* scaleType = conf.GetString(key, ""); + + if (!strcasecmp(scaleType, "")) + { + sprintf(key, "::scale_type_x%u", i); + const char* scaleTypeX = conf.GetString(key, ""); + pass.scale_type_x = scale_string_to_enum(scaleTypeX, i == shader_count - 1); + + sprintf(key, "::scale_type_y%u", i); + const char* scaleTypeY = conf.GetString(key, ""); + pass.scale_type_y = scale_string_to_enum(scaleTypeY, i == shader_count - 1); + } + else + { + int scale_type = scale_string_to_enum(scaleType, i == shader_count - 1); + pass.scale_type_x = scale_type; + pass.scale_type_y = scale_type; + } + + sprintf(key, "::scale%u", i); + const char* scaleFloat = conf.GetString(key, ""); + if (!strcasecmp(scaleFloat, "")) + { + sprintf(key, "::scale_x%u", i); + const char* scaleFloatX = conf.GetString(key, "1.0"); + pass.scale_x = atof(scaleFloatX); + sprintf(key, "::scale_y%u", i); + const char* scaleFloatY = conf.GetString(key, "1.0"); + pass.scale_y = atof(scaleFloatY); + } + else + { + pass.scale_x = pass.scale_y = atof(scaleFloat); + } + + sprintf(key, "::shader%u", i); + strcpy(pass.filename, conf.GetString(key, "")); + + sprintf(key, "::frame_count_mod%u", i); + pass.frame_count_mod = conf.GetInt(key, 0); + + sprintf(key, "::float_framebuffer%u", i); + pass.fp = conf.GetBool(key); + + this->pass.push_back(pass); + } + + char* ids = conf.GetStringDup("::textures", ""); + + char* id = strtok(ids, ";"); + + while (id != NULL) + { + GLSLLut lut; + + sprintf(key, "::%s", id); + strcpy(lut.id, id); + strcpy(lut.filename, conf.GetString(key, "")); + sprintf(key, "::%s_linear", id); + lut.filter = (conf.GetBool(key, false)) ? GL_LINEAR : GL_NEAREST; + this->lut.push_back(lut); + + id = strtok(NULL, ";"); + } + free(ids); + + return true; +} + +static void strip_parameter_pragmas(char *buffer) +{ + /* #pragma parameter lines tend to have " characters in them, + * which is not legal GLSL. */ + char *s = strstr(buffer, "#pragma parameter"); + + while (s) + { + /* #pragmas have to be on a single line, + * so we can just replace the entire line with spaces. */ + while (*s != '\0' && *s != '\n') + *s++ = ' '; + s = strstr(s, "#pragma parameter"); + } +} + +static GLuint compile_shader (char *program, const char *defines, GLuint type, GLuint *out) +{ + char info_log[1024]; + char *ptr = program; + std::string complete_program = ""; + char version[32]; + const char *existing_version = strstr(ptr, "#version"); + + if (existing_version) + { + unsigned version_no = (unsigned) strtoul (existing_version + 8, &ptr, 10); + snprintf (version, 32, "#version %u\n", version_no); + } + else + { + snprintf (version, 32, "#version 150\n"); + } + + complete_program += version; + complete_program += defines; + complete_program += ptr; + + GLuint shader = glCreateShader (type); + GLint status; + GLint length = complete_program.length(); + GLchar *prog = (GLchar *) complete_program.c_str(); + + glShaderSource (shader, 1, &prog, &length); + glCompileShader (shader); + glGetShaderiv (shader, GL_COMPILE_STATUS, &status); + + glGetShaderInfoLog (shader, 1024, NULL, info_log); + if (info_log && *info_log) + printf ("%s\n", info_log); + *out = shader; + + return status; +} + +bool GLSLShader::load_shader (char *filename) +{ + char shader_path[PATH_MAX]; + char temp[PATH_MAX]; + GLint status; + char log[1024]; + + if (this->pass.size()) + return false; + + if (!filename || *filename == '\0') + return false; + + strcpy(shader_path, filename); + reduce_to_path(shader_path); + + chdir(shader_path); + if (!load_shader_file(filename)) + return false; + + for (unsigned int i = 1; i < pass.size(); i++) + { + GLSLPass *p = &pass[i]; + GLuint vertex_shader = 0, fragment_shader = 0; + + realpath(p->filename, temp); + + char *contents = read_file (temp); + if (!contents) + { + printf("Couldn't read shader file %s\n", temp); + return false; + } + + strip_parameter_pragmas(contents); + + if (!compile_shader (contents, + "#define VERTEX\n", // #define PARAMETER_UNIFORM\n", + GL_VERTEX_SHADER, + &vertex_shader) || !vertex_shader) + { + printf("Couldn't compile vertex shader.\n"); + return false; + } + + if (!compile_shader (contents, + "#define FRAGMENT\n", // #define PARAMETER_UNIFORM\n", + GL_FRAGMENT_SHADER, + &fragment_shader) || !fragment_shader) + { + printf("Couldn't compile fragment shader\n"); + return false; + } + + p->program = glCreateProgram (); + + glAttachShader (p->program, vertex_shader); + glAttachShader (p->program, fragment_shader); + + glLinkProgram (p->program); + glGetProgramiv (p->program, GL_LINK_STATUS, &status); + glGetProgramInfoLog(p->program, 1024, NULL, log); + if (log && *log) + printf ("%s\n", log); + + glDeleteShader (vertex_shader); + glDeleteShader (fragment_shader); + + if (status != GL_TRUE) + { + printf ("Failed to link program\n"); + glDeleteProgram (p->program); + + return false; + } + + glGenFramebuffers(1, &p->fbo); + glGenTextures(1, &p->texture); + glBindTexture(GL_TEXTURE_2D, p->texture); + 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, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + + p->frame_count = 0; + } + + for (unsigned int i = 0; i < lut.size(); i++) + { + GLSLLut *l = &lut[i]; + /* generate texture for the lut and apply specified filter setting + */ + glGenTextures(1, &l->texture); + glBindTexture(GL_TEXTURE_2D, l->texture); + 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, l->filter); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, l->filter); + + realpath(l->filename, temp); + + // simple file extension png/tga decision + int length = strlen(temp); + if (length > 4) + { + if (!strcasecmp(&temp[length - 4], ".png")) + { + int width, height; + bool hasAlpha; + GLubyte* texData; + + if (loadPngImage(temp, width, height, hasAlpha, &texData)) + { + glPixelStorei(GL_UNPACK_ROW_LENGTH, width); + glTexImage2D(GL_TEXTURE_2D, + 0, + GL_RGBA, + width, + height, + 0, + hasAlpha ? GL_RGBA : GL_RGB, + GL_UNSIGNED_BYTE, + texData); + free(texData); + } + else + { + printf ("Failed to load PNG LUT: %s\n", temp); + } + } + else if (!strcasecmp(&temp[length - 4], ".tga")) + { + STGA stga; + + if (loadTGA(temp, stga)) + { + glPixelStorei(GL_UNPACK_ROW_LENGTH, stga.width); + glTexImage2D(GL_TEXTURE_2D, + 0, + GL_RGBA, + stga.width, + stga.height, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + stga.data); + } + else + { + printf ("Failed to load TGA LUT: %s\n", temp); + } + } + } + } + + glActiveTexture(GL_TEXTURE1); + glEnable(GL_TEXTURE_COORD_ARRAY); + glTexCoordPointer(2, GL_FLOAT, 0, lut_coords); + glActiveTexture(GL_TEXTURE0); + + register_uniforms(); + + prev_frame.resize (max_prev_frame); + + for (unsigned int i = 0; i < prev_frame.size(); i++) + { + glGenTextures(1, &(prev_frame[i].texture)); + prev_frame[i].width = prev_frame[i].height = 0; + } + + glGenBuffers(1, &vbo); + + frame_count = 0; + + return true; +} + +void GLSLShader::render(GLuint &orig, int width, int height, int viewport_width, int viewport_height) +{ + frame_count++; + + /* set up our dummy pass for easier loop code + */ + pass[0].texture = orig; + pass[0].width = width; + pass[0].height = height; + + /* loop through all real passes + */ + for (unsigned int i = 1; i < pass.size(); i++) + { + switch (pass[i].scale_type_x) + { + case GLSL_ABSOLUTE: + pass[i].width = pass[i].scale_x; + break; + case GLSL_SOURCE: + pass[i].width = pass[i-1].width * pass[i].scale_x; + break; + case GLSL_VIEWPORT: + pass[i].width = viewport_width * pass[i].scale_x; + break; + default: + pass[i].width = viewport_width; + } + + switch (pass[i].scale_type_y) + { + case GLSL_ABSOLUTE: + pass[i].height = pass[i].scale_y; + break; + case GLSL_SOURCE: + pass[i].height = pass[i - 1].height * pass[i].scale_y; + break; + case GLSL_VIEWPORT: + pass[i].height = viewport_height * pass[i].scale_y; + break; + default: + pass[i].height = viewport_height; + } + + /* set size of output texture + */ + glBindTexture(GL_TEXTURE_2D, pass[i].texture); + + glTexImage2D(GL_TEXTURE_2D, + 0, + (pass[i].fp ? GL_RGBA32F : GL_RGBA), + (unsigned int) pass[i].width, + (unsigned int) pass[i].height, + 0, + GL_RGBA, + GL_UNSIGNED_INT_8_8_8_8, + NULL); + + // viewport determines the area we render into the output texture + glViewport(0, 0, pass[i].width, pass[i].height); + + // set up framebuffer and attach output texture + + glBindFramebuffer(GL_FRAMEBUFFER, pass[i].fbo); + glFramebufferTexture2D(GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, + pass[i].texture, + 0); + + // set up input texture (output of previous pass) and apply filter settings + glBindTexture(GL_TEXTURE_2D, pass[i - 1].texture); + glPixelStorei(GL_UNPACK_ROW_LENGTH, (GLint) pass[i - 1].width); + glTexParameteri(GL_TEXTURE_2D, + GL_TEXTURE_MAG_FILTER, + pass[i].filter == GLSL_UNDEFINED ? GL_NEAREST : pass[i].filter); + glTexParameteri(GL_TEXTURE_2D, + GL_TEXTURE_MIN_FILTER, + pass[i].filter == GLSL_UNDEFINED ? GL_NEAREST : pass[i].filter); + + glTexCoordPointer(2, GL_FLOAT, 0, tex_coords); + + glUseProgram (pass[i].program); + set_shader_vars(i); + + glClearColor(0.0f, 0.0f, 0.0f, 0.5f); + glClear(GL_COLOR_BUFFER_BIT); + glDrawArrays(GL_QUADS, 0, 4); + + /* reset client states enabled during setShaderVars + */ + clear_shader_vars(); + } + + /* disable framebuffer + */ + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glActiveTexture(GL_TEXTURE0); + glUseProgram (0); + + /* set last PREV texture as original, push current texture and + sizes to the front of the PREV deque and make sure the new + original texture has the same size as the old one + */ + + if (prev_frame.size() > 0) + { + GLint internal_format; + orig = prev_frame.back().texture; + prev_frame.pop_back(); + + GLSLPass newprevframe; + newprevframe.width = width; + newprevframe.height = height; + newprevframe.texture = pass[0].texture; + prev_frame.push_front(newprevframe); + glBindTexture(GL_TEXTURE_2D, newprevframe.texture); + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, &internal_format); + + glBindTexture(GL_TEXTURE_2D, orig); + glPixelStorei(GL_UNPACK_ROW_LENGTH, width); + glTexImage2D(GL_TEXTURE_2D, + 0, + internal_format, + width, + height, + 0, + GL_RGB, + GL_UNSIGNED_BYTE, + NULL); + } + + glBindTexture(GL_TEXTURE_2D, pass.back().texture); + glTexParameteri(GL_TEXTURE_2D, + GL_TEXTURE_MAG_FILTER, + pass.back().filter == GLSL_UNDEFINED ? (gui_config->bilinear_filter ? GL_LINEAR : GL_NEAREST) : pass.back().filter); + glTexParameteri(GL_TEXTURE_2D, + GL_TEXTURE_MIN_FILTER, + pass.back().filter == GLSL_UNDEFINED ? (gui_config->bilinear_filter ? GL_LINEAR : GL_NEAREST) : pass.back().filter); + + glPixelStorei(GL_UNPACK_ROW_LENGTH, (GLint)pass.back().width); + glTexCoordPointer(2, GL_FLOAT, 0, inv_coords); +} + +void GLSLShader::register_uniforms () +{ + max_prev_frame = 0; + char varname[100]; + + for (unsigned int i = 1; i < pass.size(); i++) + { + GLSLUniforms *u = &pass[i].unif; + GLuint program = pass[i].program; + + glUseProgram (program); + + GLint mvp = glGetUniformLocation (program, "MVPMatrix"); + if (mvp > -1) + glUniformMatrix4fv(mvp, 1, GL_FALSE, mvp_ortho); + + u->Texture = glGetUniformLocation (program, "Texture"); + u->InputSize = glGetUniformLocation (program, "InputSize"); + u->OutputSize = glGetUniformLocation (program, "OutputSize"); + u->TextureSize = glGetUniformLocation (program, "TextureSize"); + + u->TexCoord = glGetAttribLocation (program, "TexCoord"); + u->LUTTexCoord = glGetAttribLocation (program, "LUTTexCoord"); + u->VertexCoord = glGetAttribLocation (program, "VertexCoord"); + + u->FrameCount = glGetUniformLocation (program, "FrameCount"); + u->FrameDirection = glGetUniformLocation (program, "FrameDirection"); + + u->OrigTexture = glGetUniformLocation (program, "OrigTexture"); + u->OrigInputSize = glGetUniformLocation (program, "OrigInputSize"); + u->OrigTextureSize = glGetUniformLocation (program, "OrigTextureSize"); + u->OrigTexCoord = glGetAttribLocation (program, "OrigTexCoord"); + + u->Prev[0].Texture = glGetUniformLocation (program, "PrevTexture"); + u->Prev[0].InputSize = glGetUniformLocation (program, "PrevInputSize"); + u->Prev[0].TextureSize = glGetUniformLocation (program, "PrevTextureSize"); + u->Prev[0].TexCoord = glGetAttribLocation (program, "PrevTexCoord"); + + if (u->Prev[0].Texture > -1) + max_prev_frame = 1; + + for (unsigned int j = 1; j < 7; j++) + { + sprintf(varname, "Prev%dTexture", j); + u->Prev[j].Texture = glGetUniformLocation (program, varname); + sprintf(varname, "Prev%dInputSize", j); + u->Prev[j].InputSize = glGetUniformLocation (program, varname); + sprintf(varname, "Prev%dTextureSize", j); + u->Prev[j].TextureSize = glGetUniformLocation (program, varname); + sprintf(varname, "Prev%dTexCoord", j); + u->Prev[j].TexCoord = glGetAttribLocation (program, varname); + + if (u->Prev[j].Texture > -1) + max_prev_frame = j + 1; + } + for (unsigned int j = 0; j < pass.size(); j++) + { + sprintf(varname, "Pass%dTexture", j); + u->Pass[j].Texture = glGetUniformLocation (program, varname); + sprintf(varname, "Pass%dInputSize", j); + u->Pass[j].InputSize = glGetUniformLocation (program, varname); + sprintf(varname, "Pass%dTextureSize", j); + u->Pass[j].TextureSize = glGetUniformLocation (program, varname); + sprintf(varname, "Pass%dTexCoord", j); + u->Pass[j].TexCoord = glGetAttribLocation (program, varname); + if (u->Pass[j].Texture) + u->max_pass = j; + + sprintf(varname, "PassPrev%dTexture", j); + u->PassPrev[j].Texture = glGetUniformLocation (program, varname); + sprintf(varname, "PassPrev%dInputSize", j); + u->PassPrev[j].InputSize = glGetUniformLocation (program, varname); + sprintf(varname, "PassPrev%dTextureSize", j); + u->PassPrev[j].TextureSize = glGetUniformLocation (program, varname); + sprintf(varname, "PassPrev%dTexCoord", j); + u->PassPrev[j].TexCoord = glGetAttribLocation (program, varname); + if (u->PassPrev[j].Texture > -1) + u->max_prevpass = j; + } + + for (unsigned int j = 0; j < lut.size(); j++) + { + u->Lut[j] = glGetUniformLocation (program, lut[j].id); + } + } + + glUseProgram (0); +} + +void GLSLShader::set_shader_vars (int p) +{ + unsigned int texunit = 0; + GLSLUniforms *u = &pass[p].unif; + + GLint mvp = glGetUniformLocation (pass[p].program, "MVPMatrix"); + if (mvp > -1) + glUniformMatrix4fv(mvp, 1, GL_FALSE, mvp_ortho); + +#define setUniform2fv(uni, val) if (uni > -1) glUniform2fv (uni, 1, val); +#define setUniform1f(uni, val) if (uni > -1) glUniform1f (uni, val); +#define setUniform1i(uni, val) if (uni > -1) glUniform1i (uni, val); +#define setTexture1i(uni, val) \ + if (uni > -1) \ + { \ + glActiveTexture (GL_TEXTURE0 + texunit); \ + glBindTexture (GL_TEXTURE_2D, val); \ + glUniform1i (uni, texunit); \ + texunit++; \ + } + /* We use non-power-of-two textures, + * so no need to mess with input size/texture size */ +#define setTexCoords(attr) \ + if (attr > -1) \ + { \ + glEnableVertexAttribArray(attr); \ + glVertexAttribPointer(attr, 2, GL_FLOAT, GL_FALSE, 0, NULL); \ + vaos.push_back (attr); \ + } + + glBindBuffer (GL_ARRAY_BUFFER, vbo); + glBufferData (GL_ARRAY_BUFFER, sizeof (GLfloat) * 8, tex_coords, GL_STATIC_DRAW); + + float inputSize[2] = { (float) pass[p - 1].width, (float) pass[p - 1].height }; + float outputSize[2] = { (float) pass[p].width, (float) pass[p].height }; + + setTexture1i (u->Texture, pass[p - 1].texture); + setUniform2fv(u->InputSize, inputSize); + setUniform2fv(u->OutputSize, outputSize); + setUniform2fv(u->TextureSize, inputSize); + + unsigned int shaderFrameCnt = frame_count; + if (pass[p].frame_count_mod) + shaderFrameCnt %= pass[p].frame_count_mod; + setUniform1i(u->FrameCount, (float) shaderFrameCnt); + setUniform1i(u->FrameDirection, top_level->user_rewind ? -1.0f : 1.0f); + + setTexCoords (u->TexCoord); + setTexCoords (u->LUTTexCoord); + setTexCoords (u->VertexCoord); + /* ORIG parameter + */ + float orig_videoSize[2] = { (float) pass[0].width, (float) pass[0].height }; + + setUniform2fv(u->OrigInputSize, orig_videoSize); + setUniform2fv(u->OrigTextureSize, orig_videoSize); + setTexture1i (u->OrigTexture, pass[0].texture); + setTexCoords (u->OrigTexCoord); + + /* PREV parameter + */ + if (max_prev_frame >= 1 && prev_frame[0].width > 0) { + float prevSize[2] = { (float) prev_frame[0].width, (float) prev_frame[0].height }; + + setUniform2fv(u->Prev[0].InputSize, prevSize); + setUniform2fv(u->Prev[0].TextureSize, prevSize); + setTexture1i (u->Prev[0].Texture, prev_frame[0].texture); + setTexCoords (u->Prev[0].TexCoord); + } + + /* PREV1-6 parameters + */ + for (unsigned int i = 1; i < prev_frame.size(); i++) + { + if (prev_frame[i].width <= 0) + break; + + float prevSize[2] = { (float) prev_frame[i].width, (float) prev_frame[i].height }; + + setUniform2fv(u->Prev[i].InputSize, prevSize); + setUniform2fv(u->Prev[i].TextureSize, prevSize); + setTexture1i (u->Prev[i].Texture, prev_frame[i].texture); + setTexCoords (u->Prev[i].TexCoord); + } + + /* LUT parameters + */ + for (unsigned int i = 0; i < lut.size(); i++) + { + setTexture1i (u->Lut[i], lut[i].texture); + } + + /* PASSX parameters, only for third pass and up + */ + if (p > 2) { + for (int i = 1; i < p - 1; i++) { + float passSize[2] = { (float) pass[i].width, (float) pass[i].height }; + setUniform2fv(u->Pass[i].InputSize, passSize); + setUniform2fv(u->Pass[i].TextureSize, passSize); + setTexture1i (u->Pass[i].Texture, pass[i].texture); + setTexCoords (u->Pass[i].TexCoord); + } + } + + /* PASSPREV parameter */ + for (int i = 0; i < p; i++) + { + float passSize[2] = { (float) pass[i].width, (float) pass[i].height }; + setUniform2fv(u->PassPrev[p - i].InputSize, passSize); + setUniform2fv(u->PassPrev[p - i].TextureSize, passSize); + setTexture1i (u->PassPrev[p - i].Texture, pass[i].texture); + setTexCoords (u->PassPrev[p - i].TexCoord); + } + + glActiveTexture (GL_TEXTURE0); + glBindBuffer (GL_ARRAY_BUFFER, 0); +} + +void GLSLShader::destroy (void) +{ + glBindTexture (GL_TEXTURE_2D, 0); + glUseProgram (0); + glActiveTexture (GL_TEXTURE0); + + for (unsigned int i = 1; i < pass.size(); i++) + { + glDeleteProgram (pass[i].program); + glDeleteTextures (1, &pass[i].texture); + glDeleteFramebuffers(1, &pass[i].fbo); + } + + for (unsigned int i = 0; i < lut.size(); i++) + { + glDeleteTextures (1, &lut[i].texture); + } + + for (unsigned int i = 0; i < prev_frame.size (); i++) + { + glDeleteTextures (1, &prev_frame[i].texture); + } + + pass.clear(); + lut.clear(); + prev_frame.clear(); +} + +void GLSLShader::clear_shader_vars (void) +{ + for (unsigned int i = 0; i < vaos.size(); i++) + glDisableVertexAttribArray (vaos[i]); + + vaos.clear(); +} + diff --git a/gtk/src/shaders/glsl.h b/gtk/src/shaders/glsl.h new file mode 100644 index 00000000..914dc536 --- /dev/null +++ b/gtk/src/shaders/glsl.h @@ -0,0 +1,118 @@ +#include +#include +#include +#include + +enum GLSLScaleType +{ + GLSL_NONE = 0, + GLSL_SOURCE, + GLSL_VIEWPORT, + GLSL_ABSOLUTE +}; + +enum GLSLFilter +{ + GLSL_UNDEFINED = 0, +}; + +typedef struct +{ + GLint Texture; + GLint InputSize; + GLint TextureSize; + GLint TexCoord; + +} GLSLUniformMetrics; + +typedef struct +{ + GLint Texture; + GLint InputSize; + GLint OutputSize; + GLint TextureSize; + + GLint FrameCount; + GLint FrameDirection; + + GLint TexCoord; + GLint LUTTexCoord; + GLint VertexCoord; + + GLint OrigTexture; + GLint OrigInputSize; + GLint OrigTextureSize; + GLint OrigTexCoord; + + unsigned int max_pass; + unsigned int max_prevpass; + GLSLUniformMetrics Prev[7]; + GLSLUniformMetrics Pass[20]; + GLSLUniformMetrics PassPrev[20]; + GLint Lut[9]; + +} GLSLUniforms; + +typedef struct +{ + char filename[PATH_MAX]; + int scale_type_x; + int scale_type_y; + float scale_x; + float scale_y; + bool fp; + int frame_count_mod; + unsigned int frame_count; + + GLuint program; + GLuint vertex_shader; + GLuint fragment_shader; + GLuint texture; + GLuint fbo; + GLuint width; + GLuint height; + GLuint filter; + + GLSLUniforms unif; +} GLSLPass; + +typedef struct +{ + char id[PATH_MAX]; + char filename[PATH_MAX]; + GLuint filter; + GLuint texture; +} GLSLLut; + +typedef struct +{ + char name[PATH_MAX]; + float min; + float max; + float def; + float step; +} GLSLParam; + +typedef struct +{ + bool load_shader (char *filename); + bool load_shader_file (char *filename); + void render (GLuint &orig, int width, int height, int viewport_width, int viewport_height); + void set_shader_vars (int pass); + void clear_shader_vars (void); + void destroy (void); + void register_uniforms (void); + + std::vector pass; + std::vector lut; + std::vector param; + int max_prev_frame; + std::deque prev_frame; + std::vector vaos; + + unsigned int frame_count; + GLuint vbo; + GLuint prev_fbo; + GLfloat *fa; + +} GLSLShader; diff --git a/gtk/src/shaders/shader_helpers.cpp b/gtk/src/shaders/shader_helpers.cpp new file mode 100644 index 00000000..21440d77 --- /dev/null +++ b/gtk/src/shaders/shader_helpers.cpp @@ -0,0 +1,223 @@ +#include +#include +#include +#include + +#include "shader_helpers.h" + +static void gl_error_callback( GLenum source, + GLenum type, + GLuint id, + GLenum severity, + GLsizei length, + const GLchar* message, + const void* userParam ) +{ + if (type== GL_DEBUG_TYPE_ERROR) + { + fprintf( stderr, "GL: %s type = 0x%x, severity = 0x%x, \n %s\n", + (type == GL_DEBUG_TYPE_ERROR ? "*ERROR*" : ""), + type, severity, message ); + } + else + { + fprintf( stderr, "GL type = 0x%x, severity = 0x%x, \n %s\n", + type, severity, message); + } + + return; +} + +void glLogErrors (void) +{ + glEnable (GL_DEBUG_OUTPUT); + glDebugMessageCallback ((GLDEBUGPROC) gl_error_callback, 0); +} + +bool loadPngImage(const char* name, + int& outWidth, + int& outHeight, + bool& outHasAlpha, + GLubyte** outData) +{ + png_structp png_ptr; + png_infop info_ptr; + unsigned int sig_read = 0; + FILE* fp; + + if ((fp = fopen(name, "rb")) == NULL) + return false; + + /* Create and initialize the png_struct + * with the desired error handler + * functions. If you want to use the + * default stderr and longjump method, + * you can supply NULL for the last + * three parameters. We also supply the + * the compiler header file version, so + * that we know if the application + * was compiled with a compatible version + * of the library. REQUIRED + */ + png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + + if (png_ptr == NULL) { + fclose(fp); + return false; + } + + /* Allocate/initialize the memory + * for image information. REQUIRED. */ + info_ptr = png_create_info_struct(png_ptr); + if (info_ptr == NULL) { + fclose(fp); + png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); + return false; + } + + /* Set error handling if you are + * using the setjmp/longjmp method + * (this is the normal method of + * doing things with libpng). + * REQUIRED unless you set up + * your own error handlers in + * the png_create_read_struct() + * earlier. + */ + if (setjmp(png_jmpbuf(png_ptr))) { + /* Free all of the memory associated + * with the png_ptr and info_ptr */ + png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); + fclose(fp); + /* If we get here, we had a + * problem reading the file */ + return false; + } + + /* Set up the output control if + * you are using standard C streams */ + png_init_io(png_ptr, fp); + + /* If we have already + * read some of the signature */ + png_set_sig_bytes(png_ptr, sig_read); + + /* + * If you have enough memory to read + * in the entire image at once, and + * you need to specify only + * transforms that can be controlled + * with one of the PNG_TRANSFORM_* + * bits (this presently excludes + * dithering, filling, setting + * background, and doing gamma + * adjustment), then you can read the + * entire image (including pixels) + * into the info structure with this + * call + * + * PNG_TRANSFORM_STRIP_16 | + * PNG_TRANSFORM_PACKING forces 8 bit + * PNG_TRANSFORM_EXPAND forces to + * expand a palette into RGB + */ + png_read_png(png_ptr, + info_ptr, + PNG_TRANSFORM_STRIP_16 | PNG_TRANSFORM_PACKING | PNG_TRANSFORM_EXPAND, + (png_voidp)NULL); + + outWidth = png_get_image_width(png_ptr, info_ptr); + outHeight = png_get_image_height(png_ptr, info_ptr); + switch (png_get_color_type(png_ptr, info_ptr)) { + case PNG_COLOR_TYPE_RGBA: + outHasAlpha = true; + break; + case PNG_COLOR_TYPE_RGB: + outHasAlpha = false; + break; + default: + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + fclose(fp); + return false; + } + unsigned int row_bytes = png_get_rowbytes(png_ptr, info_ptr); + *outData = (unsigned char*)malloc(row_bytes * outHeight); + + png_bytepp row_pointers = png_get_rows(png_ptr, info_ptr); + + for (int i = 0; i < outHeight; i++) { + memcpy(*outData + (row_bytes * i), row_pointers[i], row_bytes); + } + + /* Clean up after the read, + * and free any memory allocated */ + png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); + + /* Close the file */ + fclose(fp); + + /* That's it */ + return true; +} + +bool loadTGA(const char* filename, STGA& tgaFile) +{ + FILE* file; + unsigned char type[4]; + unsigned char info[6]; + + file = fopen(filename, "rb"); + + if (!file) + return false; + + fread(&type, sizeof(char), 3, file); + fseek(file, 12, SEEK_SET); + fread(&info, sizeof(char), 6, file); + + // image type either 2 (color) or 3 (greyscale) + if (type[1] != 0 || (type[2] != 2 && type[2] != 3)) { + fclose(file); + return false; + } + + tgaFile.width = info[0] + info[1] * 256; + tgaFile.height = info[2] + info[3] * 256; + tgaFile.byteCount = info[4] / 8; + + if (tgaFile.byteCount != 3 && tgaFile.byteCount != 4) { + fclose(file); + return false; + } + + long imageSize = tgaFile.width * tgaFile.height * tgaFile.byteCount; + + // allocate memory for image data + unsigned char* tempBuf = new unsigned char[imageSize]; + tgaFile.data = new unsigned char[tgaFile.width * tgaFile.height * 4]; + + // read in image data + fread(tempBuf, sizeof(unsigned char), imageSize, file); + + // swap line order and convert to RBGA + for (int i = 0; i < tgaFile.height; i++) { + unsigned char* source = tempBuf + tgaFile.width * (tgaFile.height - 1 - i) * tgaFile.byteCount; + unsigned char* destination = tgaFile.data + tgaFile.width * i * 4; + for (int j = 0; j < tgaFile.width; j++) { + destination[0] = source[2]; + destination[1] = source[1]; + destination[2] = source[0]; + destination[3] = tgaFile.byteCount == 4 ? source[3] : 0xff; + source += tgaFile.byteCount; + destination += 4; + } + } + delete[] tempBuf; + tgaFile.byteCount = 4; + + // close file + fclose(file); + + return true; +} + diff --git a/gtk/src/shaders/shader_helpers.h b/gtk/src/shaders/shader_helpers.h new file mode 100644 index 00000000..e2f3033b --- /dev/null +++ b/gtk/src/shaders/shader_helpers.h @@ -0,0 +1,44 @@ +#ifndef __IMAGE_FILE_FORMATS_H +#define __IMAGE_FILE_FORMATS_H + +#include + +typedef struct _STGA +{ + _STGA() + { + data = (unsigned char*)0; + width = 0; + height = 0; + byteCount = 0; + } + + ~_STGA() + { + delete[] data; + data = 0; + } + + void destroy() + { + delete[] data; + data = 0; + } + + int width; + int height; + unsigned char byteCount; + unsigned char* data; +} STGA; + +bool loadPngImage(const char* name, + int& outWidth, + int& outHeight, + bool& outHasAlpha, + GLubyte** outData); + +bool loadTGA(const char* filename, STGA& tgaFile); + +void glLogErrors (void); + +#endif // __IMAGE_FILE_FORMATS_H