Rudimentary GLSL Shader support.
This commit is contained in:
parent
e0de36dbb6
commit
9da5a83550
@ -235,9 +235,11 @@ if OPENGL
|
|||||||
snes9x_gtk_SOURCES += \
|
snes9x_gtk_SOURCES += \
|
||||||
src/gtk_display_driver_opengl.cpp \
|
src/gtk_display_driver_opengl.cpp \
|
||||||
src/gtk_display_driver_opengl.h \
|
src/gtk_display_driver_opengl.h \
|
||||||
src/Cg/CGLCG.cpp \
|
src/shaders/CGLCG.cpp \
|
||||||
src/Cg/cgFunctions.cpp \
|
src/shaders/cgFunctions.cpp \
|
||||||
src/Cg/CCGShader.cpp
|
src/shaders/CCGShader.cpp \
|
||||||
|
src/shaders/glsl.cpp \
|
||||||
|
src/shaders/shader_helpers.cpp
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if XV
|
if XV
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
#include "gtk_display.h"
|
#include "gtk_display.h"
|
||||||
#include "gtk_display_driver_opengl.h"
|
#include "gtk_display_driver_opengl.h"
|
||||||
|
|
||||||
#include "Cg/CGLCG.h"
|
#include "shaders/CGLCG.h"
|
||||||
|
|
||||||
S9xOpenGLDisplayDriver::S9xOpenGLDisplayDriver (Snes9xWindow *window,
|
S9xOpenGLDisplayDriver::S9xOpenGLDisplayDriver (Snes9xWindow *window,
|
||||||
Snes9xConfig *config)
|
Snes9xConfig *config)
|
||||||
@ -48,7 +48,7 @@ S9xOpenGLDisplayDriver::update (int width, int height, int yoffset)
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (using_cg_shaders)
|
if (using_cg_shaders || using_glsl_shaders)
|
||||||
{
|
{
|
||||||
glBindTexture (tex_target, texmap);
|
glBindTexture (tex_target, texmap);
|
||||||
}
|
}
|
||||||
@ -233,7 +233,12 @@ S9xOpenGLDisplayDriver::update (int width, int height, int yoffset)
|
|||||||
texcoords[4] = texcoords[2];
|
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;
|
xySize texture_size, input_size, viewport_size;
|
||||||
texture_size.x = texture_width;
|
texture_size.x = texture_width;
|
||||||
@ -376,8 +381,30 @@ S9xOpenGLDisplayDriver::load_shaders (const char *shader_file)
|
|||||||
xmlDoc *xml_doc = NULL;
|
xmlDoc *xml_doc = NULL;
|
||||||
xmlNodePtr node = NULL;
|
xmlNodePtr node = NULL;
|
||||||
char *fragment = NULL, *vertex = NULL;
|
char *fragment = NULL, *vertex = NULL;
|
||||||
|
const char *extensions = (const char *) glGetString (GL_EXTENSIONS);
|
||||||
|
|
||||||
int length = strlen (shader_file);
|
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")) ||
|
if ((length > 4 && !strcasecmp(shader_file + length - 4, ".cgp")) ||
|
||||||
(length > 3 && !strcasecmp(shader_file + length - 3, ".cg")))
|
(length > 3 && !strcasecmp(shader_file + length - 3, ".cg")))
|
||||||
{
|
{
|
||||||
@ -480,6 +507,8 @@ S9xOpenGLDisplayDriver::load_shaders (const char *shader_file)
|
|||||||
int
|
int
|
||||||
S9xOpenGLDisplayDriver::opengl_defaults (void)
|
S9xOpenGLDisplayDriver::opengl_defaults (void)
|
||||||
{
|
{
|
||||||
|
const char *extensions = (const char *) glGetString (GL_EXTENSIONS);
|
||||||
|
|
||||||
using_pbos = 0;
|
using_pbos = 0;
|
||||||
if (config->use_pbos)
|
if (config->use_pbos)
|
||||||
{
|
{
|
||||||
@ -497,8 +526,11 @@ S9xOpenGLDisplayDriver::opengl_defaults (void)
|
|||||||
|
|
||||||
using_shaders = 0;
|
using_shaders = 0;
|
||||||
using_cg_shaders = 0;
|
using_cg_shaders = 0;
|
||||||
|
using_glsl_shaders = 0;
|
||||||
cg_context = NULL;
|
cg_context = NULL;
|
||||||
cg_shader = NULL;
|
cg_shader = NULL;
|
||||||
|
glsl_shader = NULL;
|
||||||
|
|
||||||
if (config->use_shaders)
|
if (config->use_shaders)
|
||||||
{
|
{
|
||||||
if (!load_shaders (config->fragment_shader))
|
if (!load_shaders (config->fragment_shader))
|
||||||
@ -516,8 +548,6 @@ S9xOpenGLDisplayDriver::opengl_defaults (void)
|
|||||||
texture_height = 1024;
|
texture_height = 1024;
|
||||||
dyn_resizing = FALSE;
|
dyn_resizing = FALSE;
|
||||||
|
|
||||||
const char *extensions = (const char *) glGetString (GL_EXTENSIONS);
|
|
||||||
|
|
||||||
if (extensions && config->npot_textures)
|
if (extensions && config->npot_textures)
|
||||||
{
|
{
|
||||||
if (!using_shaders && strstr (extensions, "_texture_rectangle"))
|
if (!using_shaders && strstr (extensions, "_texture_rectangle"))
|
||||||
@ -800,7 +830,12 @@ S9xOpenGLDisplayDriver::deinit (void)
|
|||||||
if (!initialized)
|
if (!initialized)
|
||||||
return;
|
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;
|
delete cg_shader;
|
||||||
cgDestroyContext (cg_context);
|
cgDestroyContext (cg_context);
|
||||||
|
@ -7,7 +7,8 @@
|
|||||||
#include <epoxy/gl.h>
|
#include <epoxy/gl.h>
|
||||||
#include <epoxy/glx.h>
|
#include <epoxy/glx.h>
|
||||||
|
|
||||||
#include "Cg/CGLCG.h"
|
#include "shaders/CGLCG.h"
|
||||||
|
#include "shaders/glsl.h"
|
||||||
|
|
||||||
#define PBO_FMT_16 0
|
#define PBO_FMT_16 0
|
||||||
#define PBO_FMT_24 1
|
#define PBO_FMT_24 1
|
||||||
@ -72,6 +73,9 @@ class S9xOpenGLDisplayDriver : public S9xDisplayDriver
|
|||||||
CGcontext cg_context;
|
CGcontext cg_context;
|
||||||
CGLCG *cg_shader;
|
CGLCG *cg_shader;
|
||||||
|
|
||||||
|
int using_glsl_shaders;
|
||||||
|
GLSLShader *glsl_shader;
|
||||||
|
|
||||||
Display *display;
|
Display *display;
|
||||||
Window xwindow;
|
Window xwindow;
|
||||||
Colormap xcolormap;
|
Colormap xcolormap;
|
||||||
|
@ -196,6 +196,7 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "../gtk_display_driver_opengl.h"
|
#include "../gtk_display_driver_opengl.h"
|
||||||
|
#include "shader_helpers.h"
|
||||||
|
|
||||||
static char* ReadShaderFileContents(const char *filename)
|
static char* ReadShaderFileContents(const char *filename)
|
||||||
{
|
{
|
||||||
@ -541,7 +542,7 @@ bool CGLCG::LoadShader(const char* shaderFile)
|
|||||||
GL_UNSIGNED_SHORT_5_6_5,
|
GL_UNSIGNED_SHORT_5_6_5,
|
||||||
NULL);
|
NULL);
|
||||||
glBindTexture(GL_TEXTURE_2D, 0);
|
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));
|
memset(prevPasses[i].texCoords, 0, sizeof(prevPasses[i].texCoords));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -721,10 +722,14 @@ void CGLCG::Render(GLuint& origTex,
|
|||||||
pass.tex = shaderPasses[0].tex;
|
pass.tex = shaderPasses[0].tex;
|
||||||
memcpy(pass.texCoords, shaderPasses[1].texcoords, sizeof(pass.texCoords));
|
memcpy(pass.texCoords, shaderPasses[1].texcoords, sizeof(pass.texCoords));
|
||||||
prevPasses.push_front(pass);
|
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);
|
glBindTexture(GL_TEXTURE_2D, origTex);
|
||||||
glTexImage2D(GL_TEXTURE_2D,
|
glTexImage2D(GL_TEXTURE_2D,
|
||||||
0,
|
0,
|
||||||
GL_RGB,
|
internal_format,
|
||||||
textureSize.x,
|
textureSize.x,
|
||||||
textureSize.y,
|
textureSize.y,
|
||||||
0,
|
0,
|
||||||
@ -741,10 +746,6 @@ void CGLCG::Render(GLuint& origTex,
|
|||||||
that will be used in the main ogl code
|
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,
|
setTexCoords(shaderPasses.size() - 1,
|
||||||
shaderPasses.back().outputSize,
|
shaderPasses.back().outputSize,
|
||||||
shaderPasses.back().textureSize,
|
shaderPasses.back().textureSize,
|
||||||
@ -923,189 +924,3 @@ void CGLCG::resetAttribParams()
|
|||||||
cgAttribParams.clear();
|
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;
|
|
||||||
}
|
|
@ -209,34 +209,6 @@ typedef struct _xySize
|
|||||||
class CGLCG
|
class CGLCG
|
||||||
{
|
{
|
||||||
private:
|
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
|
typedef struct _shaderPass
|
||||||
{
|
{
|
||||||
cgScaleParams scaleParams;
|
cgScaleParams scaleParams;
|
||||||
@ -293,12 +265,6 @@ private:
|
|||||||
bool topdown = false);
|
bool topdown = false);
|
||||||
void setShaderVars(int pass);
|
void setShaderVars(int pass);
|
||||||
void resetAttribParams();
|
void resetAttribParams();
|
||||||
bool loadPngImage(const char* name,
|
|
||||||
int& outWidth,
|
|
||||||
int& outHeight,
|
|
||||||
bool& outHasAlpha,
|
|
||||||
GLubyte** outData);
|
|
||||||
bool loadTGA(const char* filename, STGA& tgaFile);
|
|
||||||
|
|
||||||
CGcontext cgContext;
|
CGcontext cgContext;
|
||||||
unsigned int frameCnt;
|
unsigned int frameCnt;
|
787
gtk/src/shaders/glsl.cpp
Normal file
787
gtk/src/shaders/glsl.cpp
Normal file
@ -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();
|
||||||
|
}
|
||||||
|
|
118
gtk/src/shaders/glsl.h
Normal file
118
gtk/src/shaders/glsl.h
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
#include <vector>
|
||||||
|
#include <deque>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <epoxy/gl.h>
|
||||||
|
|
||||||
|
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<GLSLPass> pass;
|
||||||
|
std::vector<GLSLLut> lut;
|
||||||
|
std::vector<GLSLParam> param;
|
||||||
|
int max_prev_frame;
|
||||||
|
std::deque<GLSLPass> prev_frame;
|
||||||
|
std::vector<GLuint> vaos;
|
||||||
|
|
||||||
|
unsigned int frame_count;
|
||||||
|
GLuint vbo;
|
||||||
|
GLuint prev_fbo;
|
||||||
|
GLfloat *fa;
|
||||||
|
|
||||||
|
} GLSLShader;
|
223
gtk/src/shaders/shader_helpers.cpp
Normal file
223
gtk/src/shaders/shader_helpers.cpp
Normal file
@ -0,0 +1,223 @@
|
|||||||
|
#include <epoxy/gl.h>
|
||||||
|
#include <png.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
||||||
|
|
44
gtk/src/shaders/shader_helpers.h
Normal file
44
gtk/src/shaders/shader_helpers.h
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
#ifndef __IMAGE_FILE_FORMATS_H
|
||||||
|
#define __IMAGE_FILE_FORMATS_H
|
||||||
|
|
||||||
|
#include <epoxy/gl.h>
|
||||||
|
|
||||||
|
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
|
Loading…
Reference in New Issue
Block a user