diff --git a/Makefile b/Makefile index 7fc09a784f..55e50cb718 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ include config.mak TARGET = ssnes -SOURCE = ssnes.c +SOURCE = ssnes.c LIBS = -lsamplerate -lsnes ifeq ($(BUILD_RSOUND), 1) @@ -21,6 +21,9 @@ ifeq ($(BUILD_OPENGL), 1) SOURCE += gl.c LIBS += -lglfw endif +ifeq ($(BUILD_FILTER), 1) + SOURCE += hqflt/hq.c +endif CFLAGS = -Wall -O2 -march=native -s diff --git a/config.h b/config.h index b35320cd4d..737f4f14a5 100644 --- a/config.h +++ b/config.h @@ -48,8 +48,8 @@ //////////////// // Windowed -static const float xscale = 3.0; // Real x res = 256 * xscale -static const float yscale = 3.0; // Real y res = 224 * yscale +static const float xscale = 4.0; // Real x res = 256 * xscale +static const float yscale = 4.0; // Real y res = 224 * yscale // Fullscreen static bool fullscreen = false; // To start in Fullscreen on not @@ -65,6 +65,15 @@ static const bool video_smooth = true; // On resize and fullscreen, rendering area will stay 8:7 static const bool force_aspect = true; +/////////// Video filters +#define FILTER_NONE 0 +#define FILTER_HQ2X 1 +#define FILTER_HQ4X 2 +//////////////////////// + +// If you change this to something other than the HQ filters, make sure that you build the filter module in config.mak. +#define VIDEO_FILTER FILTER_NONE + //////////////// // Audio diff --git a/config.mak b/config.mak index b3926eb5e8..04fb973188 100644 --- a/config.mak +++ b/config.mak @@ -1,9 +1,11 @@ BUILD_OPENGL = 1 +BUILD_FILTER = 0 BUILD_RSOUND = 0 BUILD_OSS = 0 BUILD_ALSA = 1 + PREFIX = /usr/local diff --git a/driver.h b/driver.h index 40c4b12d90..40620a45e1 100644 --- a/driver.h +++ b/driver.h @@ -38,6 +38,7 @@ typedef struct video_info bool vsync; bool force_aspect; bool smooth; + int input_scale; // HQ2X => 2, HQ4X => 4, None => 1 } video_info_t; typedef struct audio_driver diff --git a/gl.c b/gl.c index 0d30faeab5..6f45be5580 100644 --- a/gl.c +++ b/gl.c @@ -200,7 +200,7 @@ static void* gl_init(video_info_t *video, input_driver_t **input) else glfwSwapInterval(0); - gl_buffer = malloc(256 * 256 * 2); + gl_buffer = malloc(256 * 256 * 2 * video->input_scale * video->input_scale); if ( !gl_buffer ) { fprintf(stderr, "Couldn't allocate memory :<\n"); @@ -218,9 +218,9 @@ static void* gl_init(video_info_t *video, input_driver_t **input) glGenTextures(1, &texture); glBindTexture(GL_TEXTURE_2D, texture); - glPixelStorei(GL_UNPACK_ROW_LENGTH, 256); + glPixelStorei(GL_UNPACK_ROW_LENGTH, 256 * video->input_scale); glTexImage2D(GL_TEXTURE_2D, - 0, GL_RGB, 256, 256, 0, GL_RGBA, + 0, GL_RGB, 256 * video->input_scale, 256 * video->input_scale, 0, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, gl_buffer); *input = (input_driver_t*)&input_glfw; diff --git a/hqflt/hq.c b/hqflt/hq.c new file mode 100644 index 0000000000..f91cbe9a87 --- /dev/null +++ b/hqflt/hq.c @@ -0,0 +1,554 @@ +/* This source code is Copyright (C) 2006-2009 by WolfWings. */ +/* It is, however, released under the ISC license. */ +/* en.wikipedia.org/wiki/ISC_Licence */ +/* --------------------------------------------------------- */ +/* It is a formula-level rederiviation of the HQ2x technique */ +/* and is, thus, not a derivative work of the original code, */ +/* only the original equations behind the code. */ + +#include + +#include "pastlib.h" + +static const uint8_t blends_2x[14*4] __attribute__ ((section (".rodata.hq"))) = { +/* 5 2 4 1 */ + + 8, 4, 4, 0, + 8, 4, 0, 4, + 8, 0, 4, 4, + 16, 0, 0, 0, + 12, 4, 0, 0, + 12, 0, 4, 0, + 12, 0, 0, 4, + + 4, 6, 6, 0, /* Interp9 */ + 12, 2, 2, 0, /* Interp7 */ + 14, 1, 1, 0, /* Interp10*/ + 10, 4, 2, 0, /* Interp6 */ + 10, 2, 4, 0, /* Interp6 */ + 4, 6, 6, 0, /* Added for secondary blending used in HQ4x */ + 8, 4, 4, 0, /* Added for secondary blending used in HQ4x */ +}; + +static const uint8_t blends_4x[14*16] __attribute__ ((section (".rodata.hq"))) = { +/* 5 2 4 1 5 2 4 1 5 2 4 1 5 2 4 1 */ + + 8, 4, 4, 0, 10, 4, 2, 0, 10, 2, 4, 0, 12, 2, 2, 0, /* Needs to be split. B */ + 10, 0, 0, 6, 10, 4, 0, 2, 12, 0, 0, 4, 14, 0, 0, 2, + 10, 0, 0, 6, 10, 0, 4, 2, 12, 0, 0, 4, 14, 0, 0, 2, + 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, /* Solid colors */ + 10, 6, 0, 0, 10, 6, 0, 0, 14, 2, 0, 0, 14, 2, 0, 0, + 10, 0, 6, 0, 14, 0, 2, 0, 10, 0, 6, 0, 14, 0, 2, 0, + 10, 0, 0, 6, 12, 0, 0, 4, 12, 0, 0, 4, 14, 0, 0, 2, + 0, 8, 8, 0, 4, 8, 4, 0, 0, 6,10, 0, 12, 2, 2, 0, /* Needs to be split. A */ + 8, 4, 4, 0, 12, 4, 0, 0, 12, 0, 4, 0, 16, 0, 0, 0, + 8, 4, 4, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, + 12, 4, 0, 0, 4,12, 0, 0, 10, 0, 4, 0, 14, 0, 2, 0, + 12, 0, 4, 0, 10, 4, 0, 0, 4, 0,12, 0, 14, 2, 0, 0, + 0, 8, 8, 0, 0,10, 6, 0, 4, 4, 8, 0, 12, 2, 2, 0, /* Needs to be split. A */ + 0, 8, 8, 0, 8, 8, 0, 0, 8, 0, 8, 0, 16, 0, 0, 0, /* Needs to be split. B */ +}; + +static const uint8_t tree_hq[0x800] __attribute__ ((section (".rodata.hq"))) = { +/* 6 6 6 6 6 6 6 6 + * 2 2 2 2 2 2 2 2 + * 4 4 4 4 4 4 4 4 + * 8 8 8 8 8 8 8 8 + */ + 0, 0, 2, 2, 1, 1,13,13, 0, 0, 2, 2, 1, 1,13, 8, /* */ + 0, 0, 2, 2, 1, 1,12, 6, 0, 0, 2, 2, 1, 1, 8, 8, /* 3 */ + 0, 0, 5,10, 4, 4,13,13, 0, 0, 5, 5,11, 4,13,13, /* 1 */ + 0, 0, 5,10, 4, 4,12,13, 0, 0, 5, 5,11, 4,13,13, /* 31 */ + 0, 0, 2, 2, 1, 1, 7, 8, 0, 0, 2, 2, 1, 1, 6, 8, /* 7 */ + 0, 0, 2, 2, 1, 1, 8, 6, 0, 0, 2, 2, 1, 1, 6, 6, /* 3 7 */ + 0, 0, 5,10, 4, 4, 7,13, 0, 0, 5, 5,11, 4,13,13, /* 17 */ + 0, 0, 5,10, 4, 4, 9, 9, 0, 0, 5,10,11,11, 9, 9, /* 317 */ + 0, 0, 2, 2, 1, 1,13, 8, 0, 0, 2, 2, 1, 1, 8, 8, /* 9 */ + 0, 0, 2, 2, 1, 1,12, 8, 0, 0, 2, 2, 1, 1, 8, 6, /* 3 9 */ + 0, 0, 5, 5, 4, 4,13,13, 0, 0, 5, 5, 4, 4,13,13, /* 1 9 */ + 0, 0, 5, 5, 4, 4,12,13, 0, 0, 5, 5, 4, 4,12,13, /* 31 9 */ + 0, 0, 2, 2, 1, 1, 7, 8, 0, 0, 2, 2, 1, 1, 8, 6, /* 79 */ + 0, 0, 2, 2, 1, 1, 8, 6, 0, 0, 2, 2, 1, 1, 6, 6, /* 3 79 */ + 0, 0, 5, 5, 4, 4, 7, 7, 0, 0, 5, 5, 4, 4,13,13, /* 179 */ + 0, 0, 5, 5, 4, 4, 9, 9, 0, 0, 5, 5, 4, 4, 9, 9, /* 3179 */ + /* DIFF26 */ + 0, 0, 2, 2, 1, 1,13,13, 0, 0, 2, 2, 1, 1,13, 8, + 0, 0, 2, 2, 1, 1,12, 6, 0, 0, 2, 2, 1, 1, 8, 8, + 0, 0, 5, 5, 4, 4,13,13, 0, 0, 5, 5,11, 4,13,13, + 0, 0, 5, 5, 4, 4,12,13, 0, 0, 5, 5,11, 4,13,13, + 0, 0, 2, 2, 1, 1, 7, 8, 0, 0, 2, 2, 1, 1, 6, 8, + 0, 0, 2, 2, 1, 1, 8, 6, 0, 0, 2, 2, 1, 1, 6, 6, + 0, 0, 5, 5, 4, 4, 7,13, 0, 0, 5, 5,11, 4,13,13, + 0, 0, 5, 5, 4, 4, 9, 9, 0, 0, 5, 5,11,11, 9, 9, + 0, 0, 2, 2, 1, 1,13, 8, 0, 0, 2, 2, 1, 1, 8, 8, + 0, 0, 2, 2, 1, 1,12, 8, 0, 0, 2, 2, 1, 1, 8, 6, + 0, 0, 5, 5, 4, 4,13,13, 0, 0, 5, 5, 4, 4,13,13, + 0, 0, 5, 5, 4, 4,12,13, 0, 0, 5, 5, 4, 4,12,13, + 0, 0, 2, 2, 1, 1, 7, 8, 0, 0, 2, 2, 1, 1, 8, 6, + 0, 0, 2, 2, 1, 1, 8, 6, 0, 0, 2, 2, 1, 1, 6, 6, + 0, 0, 5, 5, 4, 4, 7, 7, 0, 0, 5, 5, 4, 4,13,13, + 0, 0, 5, 5, 4, 4, 9, 9, 0, 0, 5, 5, 4, 4, 9, 9, + /* DIFF24 */ + 0, 0, 2, 2, 1, 1, 6, 3, 0, 0, 2, 2, 1, 1, 3, 6, + 0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6, + 0, 0, 5,10, 4, 4, 3, 3, 0, 0, 5, 5,11, 4, 3, 3, + 0, 0, 5,10, 4, 4, 3, 3, 0, 0, 5, 5,11, 4, 3, 3, + 0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6, + 0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6, + 0, 0, 5,10, 4, 4, 3, 3, 0, 0, 5, 5,11, 4, 3, 3, + 0, 0, 5,10, 4, 4, 3, 3, 0, 0, 5,10,11,11, 3, 3, + 0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6, + 0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6, + 0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5, 4, 4, 3, 3, + 0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5, 4, 4, 3, 3, + 0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6, + 0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6, + 0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5, 4, 4, 3, 3, + 0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5, 4, 4, 3, 3, + /* DIFF26 DIFF24 */ + 0, 0, 2, 2, 1, 1, 6, 3, 0, 0, 2, 2, 1, 1, 3, 6, + 0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6, + 0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5,11, 4, 3, 3, + 0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5,11, 4, 3, 3, + 0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6, + 0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6, + 0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5,11, 4, 3, 3, + 0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5,11,11, 3, 3, + 0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6, + 0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6, + 0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5, 4, 4, 3, 3, + 0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5, 4, 4, 3, 3, + 0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6, + 0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6, + 0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5, 4, 4, 3, 3, + 0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5, 4, 4, 3, 3, + /* DIFF84 */ + 0, 0, 2, 2, 1, 1,13,13, 0, 0, 2, 2, 1, 1,13, 8, + 0, 0, 2, 2, 1, 1,12, 6, 0, 0, 2, 2, 1, 1, 8, 8, + 0, 0, 5,10, 4, 4,13,13, 0, 0, 5, 5, 4, 4,13,13, + 0, 0, 5,10, 4, 4,12,13, 0, 0, 5, 5, 4, 4,13,13, + 0, 0, 2, 2, 1, 1, 7, 8, 0, 0, 2, 2, 1, 1, 6, 8, + 0, 0, 2, 2, 1, 1, 8, 6, 0, 0, 2, 2, 1, 1, 6, 6, + 0, 0, 5,10, 4, 4, 7,13, 0, 0, 5, 5, 4, 4,13,13, + 0, 0, 5,10, 4, 4, 9, 9, 0, 0, 5,10, 4, 4, 9, 9, + 0, 0, 2, 2, 1, 1,13, 8, 0, 0, 2, 2, 1, 1, 8, 8, + 0, 0, 2, 2, 1, 1,12, 8, 0, 0, 2, 2, 1, 1, 8, 6, + 0, 0, 5, 5, 4, 4,13,13, 0, 0, 5, 5, 4, 4,13,13, + 0, 0, 5, 5, 4, 4,12,13, 0, 0, 5, 5, 4, 4,12,13, + 0, 0, 2, 2, 1, 1, 7, 8, 0, 0, 2, 2, 1, 1, 8, 6, + 0, 0, 2, 2, 1, 1, 8, 6, 0, 0, 2, 2, 1, 1, 6, 6, + 0, 0, 5, 5, 4, 4, 7, 7, 0, 0, 5, 5, 4, 4,13,13, + 0, 0, 5, 5, 4, 4, 9, 9, 0, 0, 5, 5, 4, 4, 9, 9, + /* DIFF26 DIFF84 */ + 0, 0, 2, 2, 1, 1,13,13, 0, 0, 2, 2, 1, 1,13, 8, + 0, 0, 2, 2, 1, 1,12, 6, 0, 0, 2, 2, 1, 1, 8, 8, + 0, 0, 5, 5, 4, 4,13,13, 0, 0, 5, 5, 4, 4,13,13, + 0, 0, 5, 5, 4, 4,12,13, 0, 0, 5, 5, 4, 4,13,13, + 0, 0, 2, 2, 1, 1, 7, 8, 0, 0, 2, 2, 1, 1, 6, 8, + 0, 0, 2, 2, 1, 1, 8, 6, 0, 0, 2, 2, 1, 1, 6, 6, + 0, 0, 5, 5, 4, 4, 7,13, 0, 0, 5, 5, 4, 4,13,13, + 0, 0, 5, 5, 4, 4, 9, 9, 0, 0, 5, 5, 4, 4, 9, 9, + 0, 0, 2, 2, 1, 1,13, 8, 0, 0, 2, 2, 1, 1, 8, 8, + 0, 0, 2, 2, 1, 1,12, 8, 0, 0, 2, 2, 1, 1, 8, 6, + 0, 0, 5, 5, 4, 4,13,13, 0, 0, 5, 5, 4, 4,13,13, + 0, 0, 5, 5, 4, 4,12,13, 0, 0, 5, 5, 4, 4,12,13, + 0, 0, 2, 2, 1, 1, 7, 8, 0, 0, 2, 2, 1, 1, 8, 6, + 0, 0, 2, 2, 1, 1, 8, 6, 0, 0, 2, 2, 1, 1, 6, 6, + 0, 0, 5, 5, 4, 4, 7, 7, 0, 0, 5, 5, 4, 4,13,13, + 0, 0, 5, 5, 4, 4, 9, 9, 0, 0, 5, 5, 4, 4, 9, 9, + /* DIFF24 DIFF84 */ + 0, 0, 2, 2, 1, 1, 6, 3, 0, 0, 2, 2, 1, 1, 3, 6, + 0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6, + 0, 0, 5,10, 4, 4, 3, 3, 0, 0, 5, 5, 4, 4, 3, 3, + 0, 0, 5,10, 4, 4, 3, 3, 0, 0, 5, 5, 4, 4, 3, 3, + 0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6, + 0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6, + 0, 0, 5,10, 4, 4, 3, 3, 0, 0, 5, 5, 4, 4, 3, 3, + 0, 0, 5,10, 4, 4, 3, 3, 0, 0, 5,10, 4, 4, 3, 3, + 0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6, + 0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6, + 0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5, 4, 4, 3, 3, + 0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5, 4, 4, 3, 3, + 0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6, + 0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6, + 0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5, 4, 4, 3, 3, + 0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5, 4, 4, 3, 3, + /* DIFF26 DIFF24 DIFF84 */ + 0, 0, 2, 2, 1, 1, 6, 3, 0, 0, 2, 2, 1, 1, 3, 6, + 0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6, + 0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5, 4, 4, 3, 3, + 0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5, 4, 4, 3, 3, + 0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6, + 0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6, + 0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5, 4, 4, 3, 3, + 0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5, 4, 4, 3, 3, + 0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6, + 0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6, + 0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5, 4, 4, 3, 3, + 0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5, 4, 4, 3, 3, + 0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6, + 0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6, + 0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5, 4, 4, 3, 3, + 0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5, 4, 4, 3, 3}; + +#define RB(y,u,v) (((y)&0x3FF)<<22)|(((u)&0x3FF)<<11)|((v)&0x3FF) +#define GO(y,v) ((((y)+64)&0x3FF)<<22)|(16<<11)|(((v)+16)&0x3FF) +static const uint32_t DiffTable[128] __attribute__ ((section (".rodata.diff"))) = { +RB(-85, 160, 80), RB(-83, 155, 77), RB(-80, 150, 75), RB(-77, 145, 72), +RB(-75, 140, 70), RB(-72, 135, 67), RB(-69, 130, 65), RB(-67, 125, 62), +RB(-64, 120, 60), RB(-61, 115, 57), RB(-59, 110, 55), RB(-56, 105, 52), +RB(-53, 100, 50), RB(-51, 95, 47), RB(-48, 90, 45), RB(-45, 85, 42), +RB(-43, 80, 40), RB(-40, 75, 37), RB(-37, 70, 35), RB(-35, 65, 32), +RB(-32, 60, 30), RB(-29, 55, 27), RB(-27, 50, 25), RB(-24, 45, 22), +RB(-21, 40, 20), RB(-19, 35, 17), RB(-16, 30, 15), RB(-13, 25, 12), +RB(-11, 20, 10), RB( -8, 15, 7), RB( -5, 10, 5), RB( -3, 5, 2), +RB( 0, 0, 0), RB( 2, -5, -2), RB( 5, -10, -5), RB( 7, -15, -7), +RB( 10, -20, -10), RB( 13, -25, -12), RB( 15, -30, -15), RB( 18, -35, -17), +RB( 21, -40, -20), RB( 23, -45, -22), RB( 26, -50, -25), RB( 29, -55, -27), +RB( 31, -60, -30), RB( 34, -65, -32), RB( 37, -70, -35), RB( 39, -75, -37), +RB( 42, -80, -40), RB( 45, -85, -42), RB( 47, -90, -45), RB( 50, -95, -47), +RB( 53,-100, -50), RB( 55,-105, -52), RB( 58,-110, -55), RB( 61,-115, -57), +RB( 63,-120, -60), RB( 66,-125, -62), RB( 69,-130, -65), RB( 71,-135, -67), +RB( 74,-140, -70), RB( 77,-145, -72), RB( 79,-150, -75), RB( 82,-155, -77), +GO(-84, -160), GO(-81, -155), GO(-78, -150), GO(-76, -145), +GO(-73, -140), GO(-70, -135), GO(-68, -130), GO(-65, -125), +GO(-63, -120), GO(-60, -115), GO(-57, -110), GO(-55, -105), +GO(-52, -100), GO(-49, -95), GO(-47, -90), GO(-44, -85), +GO(-42, -80), GO(-39, -75), GO(-36, -70), GO(-34, -65), +GO(-31, -60), GO(-28, -55), GO(-26, -50), GO(-23, -45), +GO(-21, -40), GO(-18, -35), GO(-15, -30), GO(-13, -25), +GO(-10, -20), GO( -7, -15), GO( -5, -10), GO( -2, -5), +GO( 0, 0), GO( 2, 4), GO( 5, 9), GO( 7, 14), +GO( 10, 19), GO( 13, 24), GO( 15, 29), GO( 18, 34), +GO( 21, 39), GO( 23, 44), GO( 26, 49), GO( 28, 54), +GO( 31, 59), GO( 34, 64), GO( 36, 69), GO( 39, 74), +GO( 42, 79), GO( 44, 84), GO( 47, 89), GO( 49, 94), +GO( 52, 99), GO( 55, 104), GO( 57, 109), GO( 60, 114), +GO( 63, 119), GO( 65, 124), GO( 68, 129), GO( 70, 134), +GO( 73, 139), GO( 76, 144), GO( 78, 149), GO( 81, 154)}; + +__inline__ static int ExpandedDiff (uint32_t c0, uint32_t c1) { + uint32_t r, g; + g = c0; + g += 0x4008020; + g -= c1; + r = DiffTable[(int)((unsigned char)g) + 0]; + r ^= (0x3FF << 11); + g >>= 10; + r += DiffTable[(int)((unsigned char)g) + 0]; + g >>= 11; + r += DiffTable[(int)((unsigned char)g) + 64]; + r &= 0xE01F03E0; + return r; +} + +#define DIFF56 0x001 +#define DIFF52 0x002 +#define DIFF54 0x004 +#define DIFF58 0x008 +#define DIFF53 0x010 +#define DIFF51 0x020 +#define DIFF57 0x040 +#define DIFF59 0x080 +#define DIFF26 0x100 +#define DIFF24 0x200 +#define DIFF84 0x400 +#define DIFF86 0x800 + +/* lastLineDiffs is previous line's 52, 53, 26, all >> 1 */ + +uint8_t lastLineDiffs[__PAST_LIBRARY_WIDTH]; + +#define RotatePattern(x) ((((x) & 0x777) << 1) | (((x) & 0x888) >> 3)) + +void ProcessHQ2x(const pixel *in, pixel *out) { + signed int y, x; + unsigned int pattern, newpattern; + uint32_t pixels[9]; + int prevline, nextline; + + bzero(lastLineDiffs, sizeof(lastLineDiffs)); + prevline = 1; + nextline = 1 + __PAST_LIBRARY_WIDTH; + y = __PAST_LIBRARY_HEIGHT - 1; + + do { + pixels[1-1] = + pixels[2-1] = RGBUnpack(in[prevline-1]); /* Pixel 2 */ + pixels[3-1] = RGBUnpack(in[prevline ]); /* Pixel 3 */ + + pixels[4-1] = + pixels[5-1] = RGBUnpack(in[ 0]); /* Pixel 5 */ + pixels[6-1] = RGBUnpack(in[ 1]); /* Pixel 6 */ + + pixels[7-1] = + pixels[8-1] = RGBUnpack(in[nextline-1]); /* Pixel 8 */ + pixels[9-1] = RGBUnpack(in[nextline ]); /* Pixel 9 */ + + pattern = 0; + + x = __PAST_LIBRARY_WIDTH - 1; + do { + newpattern = 0; + if (pattern & DIFF26) newpattern |= DIFF51; + if (pattern & DIFF56) newpattern |= DIFF54; + if (pattern & DIFF86) newpattern |= DIFF57; + if (pattern & DIFF53) newpattern |= DIFF24; + if (pattern & DIFF59) newpattern |= DIFF84; + + if (lastLineDiffs[x] & (DIFF52 >> 1)) newpattern |= DIFF58; + if (lastLineDiffs[x] & (DIFF26 >> 1)) newpattern |= DIFF59; + if (lastLineDiffs[x] & (DIFF53 >> 1)) newpattern |= DIFF86; + + pattern = newpattern; + + if (ExpandedDiff(pixels[5-1], pixels[2-1])) pattern |= DIFF52; + if (ExpandedDiff(pixels[5-1], pixels[3-1])) pattern |= DIFF53; + if (ExpandedDiff(pixels[5-1], pixels[6-1])) pattern |= DIFF56; + if (ExpandedDiff(pixels[2-1], pixels[6-1])) pattern |= DIFF26; + lastLineDiffs[x] = pattern >> 1; + + newpattern = tree_hq[pattern & 0x7FF]; + pattern = RotatePattern(pattern); + out[ 0] = + RGBPack(((pixels[5-1] * blends_2x[(newpattern * 4) + 0]) + + (pixels[2-1] * blends_2x[(newpattern * 4) + 1]) + + (pixels[4-1] * blends_2x[(newpattern * 4) + 2]) + + (pixels[1-1] * blends_2x[(newpattern * 4) + 3])) / 16); + + newpattern = tree_hq[pattern & 0x7FF]; + pattern = RotatePattern(pattern); + out[ 1] = + RGBPack(((pixels[5-1] * blends_2x[(newpattern * 4) + 0]) + + (pixels[6-1] * blends_2x[(newpattern * 4) + 1]) + + (pixels[2-1] * blends_2x[(newpattern * 4) + 2]) + + (pixels[3-1] * blends_2x[(newpattern * 4) + 3])) / 16); + + newpattern = tree_hq[pattern & 0x7FF]; + pattern = RotatePattern(pattern); + out[(__PAST_LIBRARY_WIDTH*2)+1] = + RGBPack(((pixels[5-1] * blends_2x[(newpattern * 4) + 0]) + + (pixels[8-1] * blends_2x[(newpattern * 4) + 1]) + + (pixels[6-1] * blends_2x[(newpattern * 4) + 2]) + + (pixels[9-1] * blends_2x[(newpattern * 4) + 3])) / 16); + + newpattern = tree_hq[pattern & 0x7FF]; + pattern = RotatePattern(pattern); + out[(__PAST_LIBRARY_WIDTH*2) ] = + RGBPack(((pixels[5-1] * blends_2x[(newpattern * 4) + 0]) + + (pixels[4-1] * blends_2x[(newpattern * 4) + 1]) + + (pixels[8-1] * blends_2x[(newpattern * 4) + 2]) + + (pixels[7-1] * blends_2x[(newpattern * 4) + 3])) / 16); + + out += 2; + in++; + x--; + + if (x == 0) { + pixels[1-1] = pixels[2-1]; + pixels[2-1] = pixels[3-1]; + pixels[4-1] = pixels[5-1]; + pixels[5-1] = pixels[6-1]; + pixels[7-1] = pixels[8-1]; + pixels[8-1] = pixels[9-1]; + continue; + } + + pixels[1-1] = pixels[2-1]; + pixels[2-1] = pixels[3-1]; + pixels[3-1] = RGBUnpack(in[prevline]); /* Pixel 3 */ + pixels[4-1] = pixels[5-1]; + pixels[5-1] = pixels[6-1]; + pixels[6-1] = RGBUnpack(in[1 ]); /* Pixel 6 */ + pixels[7-1] = pixels[8-1]; + pixels[8-1] = pixels[9-1]; + pixels[9-1] = RGBUnpack(in[nextline]); /* Pixel 9 */ + } while (x >= 0); + + prevline = 1 - __PAST_LIBRARY_WIDTH; + out += (__PAST_LIBRARY_WIDTH * 2); + + y--; + if (y > 0) { + continue; + } + + nextline = 0; + } while (y >= 0); +} + +void ProcessHQ4x(const pixel *in, pixel *out) { + signed int y, x; + unsigned int pattern, newpattern; + uint32_t pixels[9]; + int prevline, nextline; + + bzero(lastLineDiffs, sizeof(lastLineDiffs)); + prevline = 1; + nextline = 1 + __PAST_LIBRARY_WIDTH; + y = __PAST_LIBRARY_HEIGHT - 1; + + do { + pixels[1-1] = + pixels[2-1] = RGBUnpack(in[prevline-1]); /* Pixel 2 */ + pixels[3-1] = RGBUnpack(in[prevline ]); /* Pixel 3 */ + + pixels[4-1] = + pixels[5-1] = RGBUnpack(in[ 0]); /* Pixel 5 */ + pixels[6-1] = RGBUnpack(in[ 1]); /* Pixel 6 */ + + pixels[7-1] = + pixels[8-1] = RGBUnpack(in[nextline-1]); /* Pixel 8 */ + pixels[9-1] = RGBUnpack(in[nextline ]); /* Pixel 9 */ + + pattern = 0; + + x = __PAST_LIBRARY_WIDTH - 1; + do { + newpattern = 0; + if (pattern & DIFF26) newpattern |= DIFF51; + if (pattern & DIFF56) newpattern |= DIFF54; + if (pattern & DIFF86) newpattern |= DIFF57; + if (pattern & DIFF53) newpattern |= DIFF24; + if (pattern & DIFF59) newpattern |= DIFF84; + + if (lastLineDiffs[x] & (DIFF52 >> 1)) newpattern |= DIFF58; + if (lastLineDiffs[x] & (DIFF26 >> 1)) newpattern |= DIFF59; + if (lastLineDiffs[x] & (DIFF53 >> 1)) newpattern |= DIFF86; + + pattern = newpattern; + + if (ExpandedDiff(pixels[5-1], pixels[2-1])) pattern |= DIFF52; + if (ExpandedDiff(pixels[5-1], pixels[3-1])) pattern |= DIFF53; + if (ExpandedDiff(pixels[5-1], pixels[6-1])) pattern |= DIFF56; + if (ExpandedDiff(pixels[2-1], pixels[6-1])) pattern |= DIFF26; + lastLineDiffs[x] = pattern >> 1; + + newpattern = tree_hq[pattern & 0x7FF]; + pattern = RotatePattern(pattern); + out[ 0] = + RGBPack(((pixels[5-1] * blends_4x[(newpattern * 16) + 0 + 0x0]) + + (pixels[2-1] * blends_4x[(newpattern * 16) + 1 + 0x0]) + + (pixels[4-1] * blends_4x[(newpattern * 16) + 2 + 0x0]) + + (pixels[1-1] * blends_4x[(newpattern * 16) + 3 + 0x0])) / 16); + out[ 1] = + RGBPack(((pixels[5-1] * blends_4x[(newpattern * 16) + 0 + 0x4]) + + (pixels[2-1] * blends_4x[(newpattern * 16) + 1 + 0x4]) + + (pixels[4-1] * blends_4x[(newpattern * 16) + 2 + 0x4]) + + (pixels[1-1] * blends_4x[(newpattern * 16) + 3 + 0x4])) / 16); + out[(__PAST_LIBRARY_WIDTH*0x4) ] = + RGBPack(((pixels[5-1] * blends_4x[(newpattern * 16) + 0 + 0x8]) + + (pixels[2-1] * blends_4x[(newpattern * 16) + 1 + 0x8]) + + (pixels[4-1] * blends_4x[(newpattern * 16) + 2 + 0x8]) + + (pixels[1-1] * blends_4x[(newpattern * 16) + 3 + 0x8])) / 16); + out[(__PAST_LIBRARY_WIDTH*0x4)+1] = + RGBPack(((pixels[5-1] * blends_4x[(newpattern * 16) + 0 + 0xC]) + + (pixels[2-1] * blends_4x[(newpattern * 16) + 1 + 0xC]) + + (pixels[4-1] * blends_4x[(newpattern * 16) + 2 + 0xC]) + + (pixels[1-1] * blends_4x[(newpattern * 16) + 3 + 0xC])) / 16); + + newpattern = tree_hq[pattern & 0x7FF]; + pattern = RotatePattern(pattern); + out[ 3] = + RGBPack(((pixels[5-1] * blends_4x[(newpattern * 16) + 0 + 0x0]) + + (pixels[6-1] * blends_4x[(newpattern * 16) + 1 + 0x0]) + + (pixels[2-1] * blends_4x[(newpattern * 16) + 2 + 0x0]) + + (pixels[3-1] * blends_4x[(newpattern * 16) + 3 + 0x0])) / 16); + out[(__PAST_LIBRARY_WIDTH*0x4)+3] = + RGBPack(((pixels[5-1] * blends_4x[(newpattern * 16) + 0 + 0x4]) + + (pixels[6-1] * blends_4x[(newpattern * 16) + 1 + 0x4]) + + (pixels[2-1] * blends_4x[(newpattern * 16) + 2 + 0x4]) + + (pixels[3-1] * blends_4x[(newpattern * 16) + 3 + 0x4])) / 16); + out[ 2] = + RGBPack(((pixels[5-1] * blends_4x[(newpattern * 16) + 0 + 0x8]) + + (pixels[6-1] * blends_4x[(newpattern * 16) + 1 + 0x8]) + + (pixels[2-1] * blends_4x[(newpattern * 16) + 2 + 0x8]) + + (pixels[3-1] * blends_4x[(newpattern * 16) + 3 + 0x8])) / 16); + out[(__PAST_LIBRARY_WIDTH*0x4)+2] = + RGBPack(((pixels[5-1] * blends_4x[(newpattern * 16) + 0 + 0xC]) + + (pixels[6-1] * blends_4x[(newpattern * 16) + 1 + 0xC]) + + (pixels[2-1] * blends_4x[(newpattern * 16) + 2 + 0xC]) + + (pixels[3-1] * blends_4x[(newpattern * 16) + 3 + 0xC])) / 16); + + newpattern = tree_hq[pattern & 0x7FF]; + pattern = RotatePattern(pattern); + out[(__PAST_LIBRARY_WIDTH*0xC)+3] = + RGBPack(((pixels[5-1] * blends_4x[(newpattern * 16) + 0 + 0x0]) + + (pixels[8-1] * blends_4x[(newpattern * 16) + 1 + 0x0]) + + (pixels[6-1] * blends_4x[(newpattern * 16) + 2 + 0x0]) + + (pixels[9-1] * blends_4x[(newpattern * 16) + 3 + 0x0])) / 16); + out[(__PAST_LIBRARY_WIDTH*0xC)+2] = + RGBPack(((pixels[5-1] * blends_4x[(newpattern * 16) + 0 + 0x4]) + + (pixels[8-1] * blends_4x[(newpattern * 16) + 1 + 0x4]) + + (pixels[6-1] * blends_4x[(newpattern * 16) + 2 + 0x4]) + + (pixels[9-1] * blends_4x[(newpattern * 16) + 3 + 0x4])) / 16); + out[(__PAST_LIBRARY_WIDTH*0x8)+3] = + RGBPack(((pixels[5-1] * blends_4x[(newpattern * 16) + 0 + 0x8]) + + (pixels[8-1] * blends_4x[(newpattern * 16) + 1 + 0x8]) + + (pixels[6-1] * blends_4x[(newpattern * 16) + 2 + 0x8]) + + (pixels[9-1] * blends_4x[(newpattern * 16) + 3 + 0x8])) / 16); + out[(__PAST_LIBRARY_WIDTH*0x8)+2] = + RGBPack(((pixels[5-1] * blends_4x[(newpattern * 16) + 0 + 0xC]) + + (pixels[8-1] * blends_4x[(newpattern * 16) + 1 + 0xC]) + + (pixels[6-1] * blends_4x[(newpattern * 16) + 2 + 0xC]) + + (pixels[9-1] * blends_4x[(newpattern * 16) + 3 + 0xC])) / 16); + + newpattern = tree_hq[pattern & 0x7FF]; + pattern = RotatePattern(pattern); + out[(__PAST_LIBRARY_WIDTH*0xC) ] = + RGBPack(((pixels[5-1] * blends_4x[(newpattern * 16) + 0 + 0x0]) + + (pixels[4-1] * blends_4x[(newpattern * 16) + 1 + 0x0]) + + (pixels[8-1] * blends_4x[(newpattern * 16) + 2 + 0x0]) + + (pixels[7-1] * blends_4x[(newpattern * 16) + 3 + 0x0])) / 16); + out[(__PAST_LIBRARY_WIDTH*0x8) ] = + RGBPack(((pixels[5-1] * blends_4x[(newpattern * 16) + 0 + 0x4]) + + (pixels[4-1] * blends_4x[(newpattern * 16) + 1 + 0x4]) + + (pixels[8-1] * blends_4x[(newpattern * 16) + 2 + 0x4]) + + (pixels[7-1] * blends_4x[(newpattern * 16) + 3 + 0x4])) / 16); + out[(__PAST_LIBRARY_WIDTH*0xC)+1] = + RGBPack(((pixels[5-1] * blends_4x[(newpattern * 16) + 0 + 0x8]) + + (pixels[4-1] * blends_4x[(newpattern * 16) + 1 + 0x8]) + + (pixels[8-1] * blends_4x[(newpattern * 16) + 2 + 0x8]) + + (pixels[7-1] * blends_4x[(newpattern * 16) + 3 + 0x8])) / 16); + out[(__PAST_LIBRARY_WIDTH*0x8)+1] = + RGBPack(((pixels[5-1] * blends_4x[(newpattern * 16) + 0 + 0xC]) + + (pixels[4-1] * blends_4x[(newpattern * 16) + 1 + 0xC]) + + (pixels[8-1] * blends_4x[(newpattern * 16) + 2 + 0xC]) + + (pixels[7-1] * blends_4x[(newpattern * 16) + 3 + 0xC])) / 16); + + out += 4; + in++; + x--; + + if (x == 0) { + pixels[1-1] = pixels[2-1]; + pixels[2-1] = pixels[3-1]; + pixels[4-1] = pixels[5-1]; + pixels[5-1] = pixels[6-1]; + pixels[7-1] = pixels[8-1]; + pixels[8-1] = pixels[9-1]; + continue; + } + + pixels[1-1] = pixels[2-1]; + pixels[2-1] = pixels[3-1]; + pixels[3-1] = RGBUnpack(in[prevline]); /* Pixel 3 */ + pixels[4-1] = pixels[5-1]; + pixels[5-1] = pixels[6-1]; + pixels[6-1] = RGBUnpack(in[1 ]); /* Pixel 6 */ + pixels[7-1] = pixels[8-1]; + pixels[8-1] = pixels[9-1]; + pixels[9-1] = RGBUnpack(in[nextline]); /* Pixel 9 */ + } while (x >= 0); + + prevline = 1 - __PAST_LIBRARY_WIDTH; + out += (__PAST_LIBRARY_WIDTH * 0xC); + + y--; + if (y > 0) { + continue; + } + + nextline = 0; + } while (y >= 0); +} diff --git a/hqflt/pastlib.h b/hqflt/pastlib.h new file mode 100644 index 0000000000..969ebcff44 --- /dev/null +++ b/hqflt/pastlib.h @@ -0,0 +1,54 @@ +/* This source code is Copyright (C) 2006-2009 by WolfWings. */ +/* It is, however, released under the ISC license. */ +/* en.wikipedia.org/wiki/ISC_Licence */ +/* --------------------------------------------------------- */ +/* It is a formula-level rederiviation of the HQ2x technique */ +/* and is, thus, not a derivative work of the original code, */ +/* only the original equations behind the code. */ + +#ifndef __PAST_LIBRARY_H +#define __PAST_LIBRARY_H + +#include + +#ifndef __PAST_LIBRARY_WIDTH +#define __PAST_LIBRARY_WIDTH 256 +#endif + +#ifndef __PAST_LIBRARY_HEIGHT +#define __PAST_LIBRARY_HEIGHT 224 +#endif + +typedef uint16_t pixel; + +/* These two functions are what need to be modified to interface other + * pixel formats with the library. They need to return values inside a + * format like this: (Red/Blue can be reversed.) + * + * 0000-00GG-GGG0-0000-0RRR-RR00-000B-BBBB + * + * This is VERY fast to create from 15-bit (NOT 16-bit) RGB/BGR, as + * below. + * + * If you can't return data in this format, you'll have to rebuild the + * ExpandedDiff function and DiffTable array to compensate, and possibly + * change the blending functions in the Process* functions to handle any + * guard-bits and masking. + */ + +static __inline__ uint32_t RGBUnpack(pixel i) { + uint32_t o = i; + o = (o * 0x10001); + o = o & 0x03E07C1F; + return o; +} +static __inline__ pixel RGBPack(uint32_t x) { + x &= 0x03E07C1F; + x |= (x >> 16); + return x; +} + +void ProcessHQ2x(const pixel *inbuffer, pixel *outbuffer); +void ProcessHQ4x(const pixel *inbuffer, pixel *outbuffer); + +#endif /* __PAST_LIBRARY_H */ diff --git a/ssnes.c b/ssnes.c index 526ff38dbb..c249af0541 100644 --- a/ssnes.c +++ b/ssnes.c @@ -26,6 +26,7 @@ #include #include "config.h" #include "driver.h" +#include "hqflt/pastlib.h" static bool video_active = true; static bool audio_active = true; @@ -101,13 +102,25 @@ static void uninit_audio(void) static void init_video_input(void) { + int scale; +#if VIDEO_FILTER == FILTER_NONE + scale = 1; +#elif VIDEO_FILTER == FILTER_HQ2X + scale = 2; +#elif VIDEO_FILTER == FILTER_HQ4X + scale = 4; +#else + scale = 1; +#endif + video_info_t video = { .width = (fullscreen) ? fullscreen_x : (256 * xscale), .height = (fullscreen) ? fullscreen_y : (224 * yscale), .fullscreen = fullscreen, .vsync = vsync, .force_aspect = force_aspect, - .smooth = video_smooth + .smooth = video_smooth, + .input_scale = scale, }; driver.video_data = driver.video->init(&video, (input_driver_t**)&(driver.input)); @@ -144,6 +157,12 @@ static void video_frame(const uint16_t *data, unsigned width, unsigned height) return; uint16_t output[width * height]; +#if VIDEO_FILTER == FILTER_HQ2X + uint16_t outputHQ2x[width * height * 2 * 2]; +#endif +#if VIDEO_FILTER == FILTER_HQ4X + uint16_t outputHQ4x[width * height * 4 * 4]; +#endif int y; for ( y = 0; y < height; y++ ) @@ -154,8 +173,22 @@ static void video_frame(const uint16_t *data, unsigned width, unsigned height) memcpy(dst, src, width * sizeof(uint16_t)); } +#if VIDEO_FILTER == FILTER_NONE if ( !driver.video->frame(driver.video_data, output, width, height) ) video_active = false; +#elif VIDEO_FILTER == FILTER_HQ2X + ProcessHQ2x(output, outputHQ2x); + if ( !driver.video->frame(driver.video_data, outputHQ2x, width * 2, height * 2) ) + video_active = false; +#elif VIDEO_FILTER == FILTER_HQ4X + ProcessHQ4x(output, outputHQ4x); + if ( !driver.video->frame(driver.video_data, outputHQ4x, width * 4, height * 4) ) + video_active = false; +#else + if ( !driver.video->frame(driver.video_data, output, width, height) ) + video_active = false; +#endif + } static void audio_sample(uint16_t left, uint16_t right)