mirror of https://github.com/bsnes-emu/bsnes.git
Scaling filters in SDL
This commit is contained in:
parent
c03ccba8db
commit
d262dde71a
13
Makefile
13
Makefile
|
@ -43,7 +43,7 @@ endif
|
|||
# Set compilation and linkage flags based on target, platform and configuration
|
||||
|
||||
CFLAGS += -Werror -Wall -std=gnu11 -ICore -D_GNU_SOURCE -DVERSION="$(VERSION)" -I. -D_USE_MATH_DEFINES
|
||||
SDL_LDFLAGS := -lSDL2
|
||||
SDL_LDFLAGS := -lSDL2 -lGL
|
||||
ifeq ($(PLATFORM),windows32)
|
||||
CFLAGS += -IWindows
|
||||
LDFLAGS += -lmsvcrt -lSDL2main -Wl,/MANIFESTFILE:NUL
|
||||
|
@ -56,7 +56,7 @@ SYSROOT := $(shell xcodebuild -sdk macosx -version Path 2> /dev/null)
|
|||
CFLAGS += -F/Library/Frameworks
|
||||
OCFLAGS += -x objective-c -fobjc-arc -Wno-deprecated-declarations -isysroot $(SYSROOT) -mmacosx-version-min=10.9
|
||||
LDFLAGS += -framework AppKit -framework PreferencePanes -framework Carbon -framework QuartzCore
|
||||
SDL_LDFLAGS := -F/Library/Frameworks -framework SDL2
|
||||
SDL_LDFLAGS := -F/Library/Frameworks -framework SDL2 -framework OpenGL
|
||||
endif
|
||||
CFLAGS += -Wno-deprecated-declarations
|
||||
ifeq ($(PLATFORM),windows32)
|
||||
|
@ -88,7 +88,7 @@ endif
|
|||
|
||||
cocoa: $(BIN)/SameBoy.app
|
||||
quicklook: $(BIN)/SameBoy.qlgenerator
|
||||
sdl: $(SDL_TARGET) $(BIN)/SDL/dmg_boot.bin $(BIN)/SDL/cgb_boot.bin $(BIN)/SDL/LICENSE $(BIN)/SDL/registers.sym $(BIN)/SDL/background.bmp
|
||||
sdl: $(SDL_TARGET) $(BIN)/SDL/dmg_boot.bin $(BIN)/SDL/cgb_boot.bin $(BIN)/SDL/LICENSE $(BIN)/SDL/registers.sym $(BIN)/SDL/background.bmp $(BIN)/SDL/Shaders
|
||||
bootroms: $(BIN)/BootROMs/cgb_boot.bin $(BIN)/BootROMs/dmg_boot.bin
|
||||
tester: $(TESTER_TARGET) $(BIN)/tester/dmg_boot.bin $(BIN)/tester/cgb_boot.bin
|
||||
all: cocoa sdl tester
|
||||
|
@ -262,14 +262,21 @@ $(BIN)/SameBoy.app/Contents/Resources/%.bin: $(BOOTROMS_DIR)/%.bin
|
|||
cp -f $^ $@
|
||||
|
||||
$(BIN)/SDL/LICENSE: LICENSE
|
||||
-@$(MKDIR) -p $(dir $@)
|
||||
cp -f $^ $@
|
||||
|
||||
$(BIN)/SDL/registers.sym: Misc/registers.sym
|
||||
-@$(MKDIR) -p $(dir $@)
|
||||
cp -f $^ $@
|
||||
|
||||
$(BIN)/SDL/background.bmp: SDL/background.bmp
|
||||
-@$(MKDIR) -p $(dir $@)
|
||||
cp -f $^ $@
|
||||
|
||||
$(BIN)/SDL/Shaders: Shaders
|
||||
-@$(MKDIR) -p $(dir $@)
|
||||
cp -rf $^ $@
|
||||
|
||||
# Boot ROMs
|
||||
|
||||
$(BIN)/BootROMs/%.bin: BootROMs/%.asm
|
||||
|
|
132
SDL/gui.c
132
SDL/gui.c
|
@ -20,6 +20,31 @@ unsigned command_parameter;
|
|||
#define MODIFIER_NAME CTRL_STRING
|
||||
#endif
|
||||
|
||||
shader_t shader;
|
||||
SDL_Rect rect;
|
||||
|
||||
void render_texture(void *pixels, void *previous)
|
||||
{
|
||||
if (renderer) {
|
||||
if (pixels) {
|
||||
SDL_UpdateTexture(texture, NULL, pixels, 160 * sizeof (uint32_t));
|
||||
}
|
||||
SDL_RenderClear(renderer);
|
||||
SDL_RenderCopy(renderer, texture, NULL, NULL);
|
||||
SDL_RenderPresent(renderer);
|
||||
}
|
||||
else {
|
||||
static void *_pixels = NULL;
|
||||
if (pixels) {
|
||||
_pixels = pixels;
|
||||
}
|
||||
glClearColor(0, 0, 0, 1);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
render_bitmap_with_shader(&shader, _pixels, previous, rect.x, rect.y, rect.w, rect.h);
|
||||
SDL_GL_SwapWindow(window);
|
||||
}
|
||||
}
|
||||
|
||||
configuration_t configuration =
|
||||
{
|
||||
{ SDL_SCANCODE_RIGHT,
|
||||
|
@ -59,7 +84,7 @@ static const char *help[] ={
|
|||
void update_viewport(void)
|
||||
{
|
||||
int win_width, win_height;
|
||||
SDL_GetWindowSize(window, &win_width, &win_height);
|
||||
SDL_GL_GetDrawableSize(window, &win_width, &win_height);
|
||||
double x_factor = win_width / 160.0;
|
||||
double y_factor = win_height / 144.0;
|
||||
|
||||
|
@ -80,9 +105,15 @@ void update_viewport(void)
|
|||
unsigned new_width = x_factor * 160;
|
||||
unsigned new_height = y_factor * 144;
|
||||
|
||||
SDL_Rect rect = (SDL_Rect){(win_width - new_width) / 2, (win_height - new_height) /2,
|
||||
rect = (SDL_Rect){(win_width - new_width) / 2, (win_height - new_height) /2,
|
||||
new_width, new_height};
|
||||
SDL_RenderSetViewport(renderer, &rect);
|
||||
|
||||
if (renderer) {
|
||||
SDL_RenderSetViewport(renderer, &rect);
|
||||
}
|
||||
else {
|
||||
glViewport(rect.x, rect.y, rect.w, rect.h);
|
||||
}
|
||||
}
|
||||
|
||||
/* Does NOT check for bounds! */
|
||||
|
@ -234,9 +265,7 @@ void cycle_scaling(unsigned index)
|
|||
configuration.scaling_mode = 0;
|
||||
}
|
||||
update_viewport();
|
||||
SDL_RenderClear(renderer);
|
||||
SDL_RenderCopy(renderer, texture, NULL, NULL);
|
||||
SDL_RenderPresent(renderer);
|
||||
render_texture(NULL, NULL);
|
||||
}
|
||||
|
||||
void cycle_scaling_backwards(unsigned index)
|
||||
|
@ -248,9 +277,7 @@ void cycle_scaling_backwards(unsigned index)
|
|||
configuration.scaling_mode--;
|
||||
}
|
||||
update_viewport();
|
||||
SDL_RenderClear(renderer);
|
||||
SDL_RenderCopy(renderer, texture, NULL, NULL);
|
||||
SDL_RenderPresent(renderer);
|
||||
render_texture(NULL, NULL);
|
||||
}
|
||||
|
||||
static void cycle_color_correction(unsigned index)
|
||||
|
@ -273,6 +300,82 @@ static void cycle_color_correction_backwards(unsigned index)
|
|||
}
|
||||
}
|
||||
|
||||
struct shader_name {
|
||||
const char *file_name;
|
||||
const char *display_name;
|
||||
} shaders[] =
|
||||
{
|
||||
{"NearestNeighbor", "Nearest Neighbor"},
|
||||
{"Bilinear", "Bilinear"},
|
||||
{"SmoothBilinear", "Smooth Bilinear"},
|
||||
{"Scale2x", "Scale2x"},
|
||||
{"Scale4x", "Scale4x"},
|
||||
{"AAScale2x", "Anti-aliased Scale2x"},
|
||||
{"AAScale4x", "Anti-aliased Scale4x"},
|
||||
{"HQ2x", "HQ2x"},
|
||||
{"OmniScale", "OmniScale"},
|
||||
{"OmniScaleLegacy", "OmniScale Legacy"},
|
||||
{"AAOmniScaleLegacy", "AA OmniScale Legacy"},
|
||||
};
|
||||
|
||||
static void cycle_filter(unsigned index)
|
||||
{
|
||||
unsigned i = 0;
|
||||
for (; i < sizeof(shaders) / sizeof(shaders[0]); i++) {
|
||||
if (strcmp(shaders[i].file_name, configuration.filter) == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
i += 1;
|
||||
if (i >= sizeof(shaders) / sizeof(shaders[0])) {
|
||||
i -= sizeof(shaders) / sizeof(shaders[0]);
|
||||
}
|
||||
|
||||
strcpy(configuration.filter, shaders[i].file_name);
|
||||
free_shader(&shader);
|
||||
if (!init_shader_with_name(&shader, configuration.filter)) {
|
||||
init_shader_with_name(&shader, "NearestNeighbor");
|
||||
}
|
||||
}
|
||||
|
||||
static void cycle_filter_backwards(unsigned index)
|
||||
{
|
||||
unsigned i = 0;
|
||||
for (; i < sizeof(shaders) / sizeof(shaders[0]); i++) {
|
||||
if (strcmp(shaders[i].file_name, configuration.filter) == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
i -= 1;
|
||||
if (i >= sizeof(shaders) / sizeof(shaders[0])) {
|
||||
i = sizeof(shaders) / sizeof(shaders[0]) - 1;
|
||||
}
|
||||
|
||||
strcpy(configuration.filter, shaders[i].file_name);
|
||||
free_shader(&shader);
|
||||
if (!init_shader_with_name(&shader, configuration.filter)) {
|
||||
init_shader_with_name(&shader, "NearestNeighbor");
|
||||
}
|
||||
|
||||
}
|
||||
const char *current_filter_name(unsigned index)
|
||||
{
|
||||
unsigned i = 0;
|
||||
for (; i < sizeof(shaders) / sizeof(shaders[0]); i++) {
|
||||
if (strcmp(shaders[i].file_name, configuration.filter) == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == sizeof(shaders) / sizeof(shaders[0])) {
|
||||
i = 0;
|
||||
}
|
||||
|
||||
return shaders[i].display_name;
|
||||
}
|
||||
|
||||
static void return_to_root_menu(unsigned index)
|
||||
{
|
||||
|
@ -282,6 +385,7 @@ static void return_to_root_menu(unsigned index)
|
|||
|
||||
static const struct menu_item graphics_menu[] = {
|
||||
{"Scaling Mode:", cycle_scaling, current_scaling_mode, cycle_scaling_backwards},
|
||||
{"Scaling Filter:", cycle_filter, current_filter_name, cycle_filter_backwards},
|
||||
{"Color Correction:", cycle_color_correction, current_color_correction_mode, cycle_color_correction_backwards},
|
||||
{"Back", return_to_root_menu},
|
||||
{NULL,}
|
||||
|
@ -399,6 +503,7 @@ static void detect_joypad_layout(unsigned index)
|
|||
static const struct menu_item joypad_menu[] = {
|
||||
{"Joypad:", cycle_joypads, current_joypad_name, cycle_joypads_backwards},
|
||||
{"Detect layout", detect_joypad_layout},
|
||||
{"Back", return_to_root_menu},
|
||||
{NULL,}
|
||||
};
|
||||
|
||||
|
@ -510,9 +615,7 @@ void run_gui(bool is_running)
|
|||
case SDL_WINDOWEVENT: {
|
||||
if (event.window.event == SDL_WINDOWEVENT_RESIZED) {
|
||||
update_viewport();
|
||||
SDL_RenderClear(renderer);
|
||||
SDL_RenderCopy(renderer, texture, NULL, NULL);
|
||||
SDL_RenderPresent(renderer);
|
||||
render_texture(NULL, NULL);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -667,10 +770,7 @@ void run_gui(bool is_running)
|
|||
break;
|
||||
}
|
||||
|
||||
SDL_UpdateTexture(texture, NULL, pixels, 160 * sizeof (uint32_t));
|
||||
SDL_RenderClear(renderer);
|
||||
SDL_RenderCopy(renderer, texture, NULL, NULL);
|
||||
SDL_RenderPresent(renderer);
|
||||
render_texture(pixels, NULL);
|
||||
}
|
||||
} while (SDL_WaitEvent(&event));
|
||||
}
|
||||
|
|
|
@ -3,11 +3,13 @@
|
|||
|
||||
#include <SDL2/SDL.h>
|
||||
#include <Core/gb.h>
|
||||
#include "shader.h"
|
||||
|
||||
extern SDL_Window *window;
|
||||
extern SDL_Renderer *renderer;
|
||||
extern SDL_Texture *texture;
|
||||
extern SDL_PixelFormat *pixel_format;
|
||||
extern shader_t shader;
|
||||
|
||||
enum scaling_mode {
|
||||
GB_SDL_SCALING_ENTIRE_WINDOW,
|
||||
|
@ -37,6 +39,8 @@ typedef struct {
|
|||
bool div_joystick;
|
||||
bool flip_joystick_bit_1;
|
||||
bool swap_joysticks_bits_1_and_2;
|
||||
|
||||
char filter[32];
|
||||
} configuration_t;
|
||||
|
||||
extern configuration_t configuration;
|
||||
|
@ -44,5 +48,6 @@ extern configuration_t configuration;
|
|||
void update_viewport(void);
|
||||
void run_gui(bool is_running);
|
||||
unsigned fix_joypad_button(unsigned button);
|
||||
void render_texture(void *pixels, void *previous);
|
||||
|
||||
#endif
|
||||
|
|
39
SDL/main.c
39
SDL/main.c
|
@ -4,6 +4,7 @@
|
|||
#include "gb.h"
|
||||
#include "utils.h"
|
||||
#include "gui.h"
|
||||
#include "shader.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
#define AUDIO_FREQUENCY 96000
|
||||
|
@ -212,10 +213,7 @@ static void handle_events(GB_gameboy_t *gb)
|
|||
|
||||
static void vblank(GB_gameboy_t *gb)
|
||||
{
|
||||
SDL_UpdateTexture(texture, NULL, pixels, 160 * sizeof (uint32_t));
|
||||
SDL_RenderClear(renderer);
|
||||
SDL_RenderCopy(renderer, texture, NULL, NULL);
|
||||
SDL_RenderPresent(renderer);
|
||||
render_texture(pixels, NULL);
|
||||
handle_events(gb);
|
||||
}
|
||||
|
||||
|
@ -358,7 +356,7 @@ restart:
|
|||
pending_command = GB_SDL_NO_COMMAND;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
#define str(x) #x
|
||||
|
@ -390,15 +388,25 @@ usage:
|
|||
|
||||
SDL_Init( SDL_INIT_EVERYTHING );
|
||||
|
||||
window = SDL_CreateWindow("SameBoy v" xstr(VERSION), SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
|
||||
160 * 2, 144 * 2, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
|
||||
SDL_SetWindowMinimumSize(window, 160, 144);
|
||||
renderer = SDL_CreateRenderer(window, -1, 0);
|
||||
|
||||
texture = SDL_CreateTexture(renderer, SDL_GetWindowPixelFormat(window), SDL_TEXTUREACCESS_STREAMING, 160, 144);
|
||||
|
||||
pixel_format = SDL_AllocFormat(SDL_GetWindowPixelFormat(window));
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
|
||||
|
||||
window = SDL_CreateWindow("SameBoy v" xstr(VERSION), SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
|
||||
160 * 2, 144 * 2, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
|
||||
SDL_SetWindowMinimumSize(window, 160, 144);
|
||||
|
||||
SDL_GLContext gl_context = SDL_GL_CreateContext(window);
|
||||
if (gl_context == NULL) {
|
||||
renderer = SDL_CreateRenderer(window, -1, 0);
|
||||
texture = SDL_CreateTexture(renderer, SDL_GetWindowPixelFormat(window), SDL_TEXTUREACCESS_STREAMING, 160, 144);
|
||||
pixel_format = SDL_AllocFormat(SDL_GetWindowPixelFormat(window));
|
||||
}
|
||||
else {
|
||||
pixel_format = SDL_AllocFormat(SDL_PIXELFORMAT_ABGR8888);
|
||||
}
|
||||
|
||||
|
||||
/* Configure Audio */
|
||||
memset(&want_aspec, 0, sizeof(want_aspec));
|
||||
want_aspec.freq = AUDIO_FREQUENCY;
|
||||
|
@ -420,6 +428,11 @@ usage:
|
|||
|
||||
SDL_EventState(SDL_DROPFILE, SDL_ENABLE);
|
||||
|
||||
if (!init_shader_with_name(&shader, configuration.filter)) {
|
||||
init_shader_with_name(&shader, "NearestNeighbor");
|
||||
}
|
||||
update_viewport();
|
||||
|
||||
if (filename == NULL) {
|
||||
run_gui(false);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,184 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "shader.h"
|
||||
#include "utils.h"
|
||||
#include <OpenGL/gl3.h>
|
||||
|
||||
static const char *vertex_shader = "\n\
|
||||
#version 150 \n\
|
||||
in vec4 aPosition;\n\
|
||||
void main(void) {\n\
|
||||
gl_Position = aPosition;\n\
|
||||
}\n\
|
||||
";
|
||||
|
||||
static GLuint create_shader(const char *source, GLenum type)
|
||||
{
|
||||
// Create the shader object
|
||||
GLuint shader = glCreateShader(type);
|
||||
// Load the shader source
|
||||
glShaderSource(shader, 1, &source, 0);
|
||||
// Compile the shader
|
||||
glCompileShader(shader);
|
||||
// Check for errors
|
||||
GLint status = 0;
|
||||
glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
|
||||
if (status == GL_FALSE) {
|
||||
GLchar messages[1024];
|
||||
glGetShaderInfoLog(shader, sizeof(messages), 0, &messages[0]);
|
||||
fprintf(stderr, "GLSL Shader Error: %s", messages);
|
||||
}
|
||||
return shader;
|
||||
}
|
||||
|
||||
static GLuint create_program(const char *vsh, const char *fsh)
|
||||
{
|
||||
// Build shaders
|
||||
GLuint vertex_shader = create_shader(vsh, GL_VERTEX_SHADER);
|
||||
GLuint fragment_shader = create_shader(fsh, GL_FRAGMENT_SHADER);
|
||||
|
||||
// Create program
|
||||
GLuint program = glCreateProgram();
|
||||
|
||||
// Attach shaders
|
||||
glAttachShader(program, vertex_shader);
|
||||
glAttachShader(program, fragment_shader);
|
||||
|
||||
// Link program
|
||||
glLinkProgram(program);
|
||||
// Check for errors
|
||||
GLint status;
|
||||
glGetProgramiv(program, GL_LINK_STATUS, &status);
|
||||
|
||||
if (status == GL_FALSE) {
|
||||
GLchar messages[1024];
|
||||
glGetProgramInfoLog(program, sizeof(messages), 0, &messages[0]);
|
||||
fprintf(stderr, "GLSL Program Error: %s", messages);
|
||||
}
|
||||
|
||||
// Delete shaders
|
||||
glDeleteShader(vertex_shader);
|
||||
glDeleteShader(fragment_shader);
|
||||
|
||||
return program;
|
||||
}
|
||||
|
||||
bool init_shader_with_name(shader_t *shader, const char *name)
|
||||
{
|
||||
static char master_shader_code[0x801] = {0,};
|
||||
static char shader_code[0x10001] = {0,};
|
||||
static char final_shader_code[0x10801] = {0,};
|
||||
static ssize_t filter_token_location = 0;
|
||||
|
||||
if (!master_shader_code[0]) {
|
||||
FILE *master_shader_f = fopen(executable_relative_path("Shaders/MasterShader.fsh"), "r");
|
||||
if (!master_shader_f) return false;
|
||||
fread(master_shader_code, 1, sizeof(master_shader_code) - 1, master_shader_f);
|
||||
fclose(master_shader_f);
|
||||
filter_token_location = strstr(master_shader_code, "{filter}") - master_shader_code;
|
||||
if (filter_token_location < 0) {
|
||||
master_shader_code[0] = 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
char shader_path[1024];
|
||||
sprintf(shader_path, "Shaders/%s.fsh", name);
|
||||
|
||||
FILE *shader_f = fopen(executable_relative_path(shader_path), "r");
|
||||
if (!shader_f) return false;
|
||||
memset(shader_code, 0, sizeof(shader_code));
|
||||
fread(shader_code, 1, sizeof(shader_code) - 1, shader_f);
|
||||
fclose(shader_f);
|
||||
|
||||
memset(final_shader_code, 0, sizeof(final_shader_code));
|
||||
memcpy(final_shader_code, master_shader_code, filter_token_location);
|
||||
strcpy(final_shader_code + filter_token_location, shader_code);
|
||||
strcat(final_shader_code + filter_token_location,
|
||||
master_shader_code + filter_token_location + sizeof("{filter}") - 1);
|
||||
|
||||
shader->program = create_program(vertex_shader, final_shader_code);
|
||||
|
||||
// Attributes
|
||||
shader->position_attribute = glGetAttribLocation(shader->program, "aPosition");
|
||||
// Uniforms
|
||||
shader->resolution_uniform = glGetUniformLocation(shader->program, "uResolution");
|
||||
shader->origin_uniform = glGetUniformLocation(shader->program, "uOrigin");
|
||||
|
||||
glGenTextures(1, &shader->texture);
|
||||
glBindTexture(GL_TEXTURE_2D, shader->texture);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
shader->texture_uniform = glGetUniformLocation(shader->program, "image");
|
||||
|
||||
glGenTextures(1, &shader->previous_texture);
|
||||
glBindTexture(GL_TEXTURE_2D, shader->previous_texture);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
shader->previous_texture_uniform = glGetUniformLocation(shader->program, "previousImage");
|
||||
|
||||
shader->mix_previous_uniform = glGetUniformLocation(shader->program, "uMixPrevious");
|
||||
|
||||
// Program
|
||||
|
||||
glUseProgram(shader->program);
|
||||
|
||||
GLuint vao;
|
||||
glGenVertexArrays(1, &vao);
|
||||
glBindVertexArray(vao);
|
||||
|
||||
GLuint vbo;
|
||||
glGenBuffers(1, &vbo);
|
||||
|
||||
// Attributes
|
||||
|
||||
|
||||
static GLfloat const quad[16] = {
|
||||
-1.f, -1.f, 0, 1,
|
||||
-1.f, +1.f, 0, 1,
|
||||
+1.f, -1.f, 0, 1,
|
||||
+1.f, +1.f, 0, 1,
|
||||
};
|
||||
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(quad), quad, GL_STATIC_DRAW);
|
||||
glEnableVertexAttribArray(shader->position_attribute);
|
||||
glVertexAttribPointer(shader->position_attribute, 4, GL_FLOAT, GL_FALSE, 0, 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void render_bitmap_with_shader(shader_t *shader, void *bitmap, void *previous,unsigned x, unsigned y, unsigned w, unsigned h)
|
||||
{
|
||||
glUseProgram(shader->program);
|
||||
glUniform2f(shader->origin_uniform, x, y);
|
||||
glUniform2f(shader->resolution_uniform, w, h);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, shader->texture);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 160, 144, 0, GL_RGBA, GL_UNSIGNED_BYTE, bitmap);
|
||||
glUniform1i(shader->texture_uniform, 0);
|
||||
glUniform1i(shader->mix_previous_uniform, previous != NULL);
|
||||
if (previous) {
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
glBindTexture(GL_TEXTURE_2D, shader->previous_texture);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 160, 144, 0, GL_RGBA, GL_UNSIGNED_BYTE, previous);
|
||||
glUniform1i(shader->previous_texture_uniform, 1);
|
||||
}
|
||||
glBindFragDataLocation(shader->program, 0, "frag_color");
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
}
|
||||
|
||||
void free_shader(shader_t *shader)
|
||||
{
|
||||
glDeleteProgram(shader->program);
|
||||
glDeleteTextures(1, &shader->texture);
|
||||
glDeleteTextures(1, &shader->previous_texture);
|
||||
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
#ifndef shader_h
|
||||
#define shader_h
|
||||
#include <OpenGL/gl3.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef struct shader_s {
|
||||
GLuint resolution_uniform;
|
||||
GLuint origin_uniform;
|
||||
GLuint texture_uniform;
|
||||
GLuint previous_texture_uniform;
|
||||
GLuint mix_previous_uniform;
|
||||
|
||||
GLuint position_attribute;
|
||||
GLuint texture;
|
||||
GLuint previous_texture;
|
||||
GLuint program;
|
||||
} shader_t;
|
||||
|
||||
bool init_shader_with_name(shader_t *shader, const char *name);
|
||||
void render_bitmap_with_shader(shader_t *shader, void *bitmap, void *previous,unsigned x, unsigned y, unsigned w, unsigned h);
|
||||
void free_shader(struct shader_s *shader);
|
||||
|
||||
#endif /* shader_h */
|
|
@ -4,14 +4,20 @@ uniform sampler2D previousImage;
|
|||
uniform bool uMixPrevious;
|
||||
|
||||
uniform vec2 uResolution;
|
||||
uniform vec2 uOrigin;
|
||||
const vec2 textureDimensions = vec2(160, 144);
|
||||
|
||||
out vec4 frag_color;
|
||||
|
||||
vec4 modified_frag_cord;
|
||||
#define gl_FragCoord modified_frag_cord
|
||||
#line 1
|
||||
{filter}
|
||||
#undef gl_FragCoord
|
||||
|
||||
void main() {
|
||||
modified_frag_cord = gl_FragCoord - vec4(uOrigin.x, uOrigin.y, 0, 0);
|
||||
|
||||
if (uMixPrevious) {
|
||||
frag_color = mix(scale(image), scale(previousImage), 0.5);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue