diff --git a/gfx/filters/Makefile b/gfx/filters/Makefile
index 36b605a025..c3f6187859 100644
--- a/gfx/filters/Makefile
+++ b/gfx/filters/Makefile
@@ -68,7 +68,7 @@ ASMFLAGS := -INEON/asm
asflags += -mfpu=neon
endif
-objects += epx.$(DYLIB) 2xsai.$(DYLIB) super2xsai.$(DYLIB) supereagle.$(DYLIB) 2xbr.$(DYLIB) darken.$(DYLIB) scale2x.$(DYLIB)
+objects += blargg_ntsc_snes_composite.$(DYLIB) epx.$(DYLIB) 2xsai.$(DYLIB) super2xsai.$(DYLIB) supereagle.$(DYLIB) 2xbr.$(DYLIB) darken.$(DYLIB) scale2x.$(DYLIB)
all: build;
diff --git a/gfx/filters/blargg_ntsc_snes_composite.c b/gfx/filters/blargg_ntsc_snes_composite.c
new file mode 100755
index 0000000000..e623a4c9d9
--- /dev/null
+++ b/gfx/filters/blargg_ntsc_snes_composite.c
@@ -0,0 +1,202 @@
+/* RetroArch - A frontend for libretro.
+ * Copyright (C) 2010-2014 - Hans-Kristian Arntzen
+ *
+ * RetroArch is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Found-
+ * ation, either version 3 of the License, or (at your option) any later version.
+ *
+ * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with RetroArch.
+ * If not, see .
+ */
+
+#include "softfilter.h"
+#include
+#include "boolean.h"
+
+#ifdef RARCH_INTERNAL
+#define softfilter_get_implementation blargg_ntsc_snes_composite_get_implementation
+#endif
+
+#include "snes_ntsc/snes_ntsc.h"
+#include "snes_ntsc/snes_ntsc.c"
+
+void filter_size(unsigned*, unsigned*);
+void filter_render(uint32_t*, uint32_t*, unsigned, const uint16_t*, unsigned, unsigned, unsigned);
+
+struct snes_ntsc_t *ntsc;
+snes_ntsc_setup_t setup;
+int burst;
+int burst_toggle;
+
+static unsigned blargg_ntsc_snes_composite_generic_input_fmts(void)
+{
+ return SOFTFILTER_FMT_RGB565;
+}
+
+static unsigned blargg_ntsc_snes_composite_generic_output_fmts(unsigned input_fmts)
+{
+ return input_fmts;
+}
+
+static unsigned blargg_ntsc_snes_composite_generic_threads(void *data)
+{
+ struct filter_data *filt = (struct filter_data*)data;
+ return filt->threads;
+}
+
+static void *blargg_ntsc_snes_composite_generic_create(unsigned in_fmt, unsigned out_fmt,
+ unsigned max_width, unsigned max_height,
+ unsigned threads, softfilter_simd_mask_t simd)
+{
+ (void)simd;
+
+ struct filter_data *filt = (struct filter_data*)calloc(1, sizeof(*filt));
+ if (!filt)
+ return NULL;
+ filt->workers = (struct softfilter_thread_data*)calloc(threads, sizeof(struct softfilter_thread_data));
+ filt->threads = threads;
+ filt->in_fmt = in_fmt;
+ if (!filt->workers)
+ {
+ free(filt);
+ return NULL;
+ }
+ return filt;
+}
+
+static void blargg_ntsc_snes_composite_initialize(void)
+{
+ static bool initialized = false;
+ if(initialized == true)
+ return;
+ initialized = true;
+
+ ntsc = (snes_ntsc_t*)malloc(sizeof *ntsc);
+ setup = snes_ntsc_composite;
+ setup.merge_fields = 1;
+ snes_ntsc_init(ntsc, &setup);
+
+ burst = 0;
+ burst_toggle = (setup.merge_fields ? 0 : 1);
+}
+
+void terminate(void)
+{
+ if(ntsc)
+ free(ntsc);
+}
+
+static void blargg_ntsc_snes_composite_generic_output(void *data, unsigned *out_width, unsigned *out_height,
+ unsigned width, unsigned height)
+{
+ blargg_ntsc_snes_composite_initialize();
+ *out_width = SNES_NTSC_OUT_WIDTH(256);
+ *out_height = height;
+}
+
+static void blargg_ntsc_snes_composite_generic_destroy(void *data)
+{
+ struct filter_data *filt = (struct filter_data*)data;
+ free(filt->workers);
+ free(filt);
+}
+
+static void blargg_ntsc_snes_composite_render_rgb565(int width, int height,
+ int first, int last,
+ uint16_t *input, int pitch, uint16_t *output, int outpitch)
+{
+ blargg_ntsc_snes_composite_initialize();
+ if(!ntsc)
+ return;
+
+ pitch >>= 1;
+ outpitch >>= 2;
+
+ if(width <= 256)
+ snes_ntsc_blit(ntsc, input, pitch, burst, width, height, output, outpitch * 2, first, last);
+ else
+ snes_ntsc_blit_hires(ntsc, input, pitch, burst, width, height, output, outpitch * 2, first, last);
+
+ burst ^= burst_toggle;
+}
+
+static void blargg_ntsc_snes_composite_rgb565(unsigned width, unsigned height,
+ int first, int last, uint16_t *src,
+ unsigned src_stride, uint16_t *dst, unsigned dst_stride)
+{
+ blargg_ntsc_snes_composite_render_rgb565(width, height,
+ first, last,
+ src, src_stride,
+ dst, dst_stride);
+
+}
+
+static void blargg_ntsc_snes_composite_work_cb_rgb565(void *data, void *thread_data)
+{
+ struct softfilter_thread_data *thr = (struct softfilter_thread_data*)thread_data;
+ uint16_t *input = (uint16_t*)thr->in_data;
+ uint16_t *output = (uint16_t*)thr->out_data;
+ unsigned width = thr->width;
+ unsigned height = thr->height;
+
+ blargg_ntsc_snes_composite_rgb565(width, height,
+ thr->first, thr->last, input, thr->in_pitch / SOFTFILTER_BPP_RGB565, output, thr->out_pitch / SOFTFILTER_BPP_RGB565);
+}
+
+static void blargg_ntsc_snes_composite_generic_packets(void *data,
+ struct softfilter_work_packet *packets,
+ void *output, size_t output_stride,
+ const void *input, unsigned width, unsigned height, size_t input_stride)
+{
+ struct filter_data *filt = (struct filter_data*)data;
+ unsigned i;
+ for (i = 0; i < filt->threads; i++)
+ {
+ struct softfilter_thread_data *thr = (struct softfilter_thread_data*)&filt->workers[i];
+
+ unsigned y_start = (height * i) / filt->threads;
+ unsigned y_end = (height * (i + 1)) / filt->threads;
+ thr->out_data = (uint8_t*)output + y_start * output_stride;
+ thr->in_data = (const uint8_t*)input + y_start * input_stride;
+ thr->out_pitch = output_stride;
+ thr->in_pitch = input_stride;
+ thr->width = width;
+ thr->height = y_end - y_start;
+
+ // Workers need to know if they can access pixels outside their given buffer.
+ thr->first = y_start;
+ thr->last = y_end == height;
+
+ if (filt->in_fmt == SOFTFILTER_FMT_RGB565)
+ packets[i].work = blargg_ntsc_snes_composite_work_cb_rgb565;
+ packets[i].thread_data = thr;
+ }
+}
+
+static const struct softfilter_implementation blargg_ntsc_snes_composite_generic = {
+ blargg_ntsc_snes_composite_generic_input_fmts,
+ blargg_ntsc_snes_composite_generic_output_fmts,
+
+ blargg_ntsc_snes_composite_generic_create,
+ blargg_ntsc_snes_composite_generic_destroy,
+
+ blargg_ntsc_snes_composite_generic_threads,
+ blargg_ntsc_snes_composite_generic_output,
+ blargg_ntsc_snes_composite_generic_packets,
+ "Blargg NTSC SNES Composite",
+ SOFTFILTER_API_VERSION,
+};
+
+const struct softfilter_implementation *softfilter_get_implementation(softfilter_simd_mask_t simd)
+{
+ (void)simd;
+ return &blargg_ntsc_snes_composite_generic;
+}
+
+#ifdef RARCH_INTERNAL
+#undef softfilter_get_implementation
+#endif
diff --git a/gfx/filters/boolean.h b/gfx/filters/boolean.h
new file mode 100644
index 0000000000..b0a20bd8d6
--- /dev/null
+++ b/gfx/filters/boolean.h
@@ -0,0 +1,33 @@
+/* RetroArch - A frontend for libretro.
+ * Copyright (C) 2010-2014 - Hans-Kristian Arntzen
+ *
+ * RetroArch is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Found-
+ * ation, either version 3 of the License, or (at your option) any later version.
+ *
+ * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with RetroArch.
+ * If not, see .
+ */
+
+#ifndef __RARCH_BOOLEAN_H
+#define __RARCH_BOOLEAN_H
+
+#ifndef __cplusplus
+
+#if defined(_MSC_VER) && !defined(SN_TARGET_PS3)
+/* Hack applied for MSVC when compiling in C89 mode as it isn't C99 compliant. */
+#define bool unsigned char
+#define true 1
+#define false 0
+#else
+#include
+#endif
+
+#endif
+
+#endif
+
diff --git a/gfx/filters/snes_ntsc/snes_ntsc.c b/gfx/filters/snes_ntsc/snes_ntsc.c
new file mode 100755
index 0000000000..6e383b61c3
--- /dev/null
+++ b/gfx/filters/snes_ntsc/snes_ntsc.c
@@ -0,0 +1,238 @@
+/* snes_ntsc 0.2.2. http://www.slack.net/~ant/ */
+
+#include "snes_ntsc.h"
+
+/* Copyright (C) 2006-2007 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+snes_ntsc_setup_t const snes_ntsc_monochrome = { 0,-1, 0, 0,.2, 0,.2,-.2,-.2,-1, 1, 0, 0 };
+snes_ntsc_setup_t const snes_ntsc_composite = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 };
+snes_ntsc_setup_t const snes_ntsc_svideo = { 0, 0, 0, 0,.2, 0,.2, -1, -1, 0, 1, 0, 0 };
+snes_ntsc_setup_t const snes_ntsc_rgb = { 0, 0, 0, 0,.2, 0,.7, -1, -1,-1, 1, 0, 0 };
+
+#define alignment_count 3
+#define burst_count 3
+#define rescale_in 8
+#define rescale_out 7
+
+#define artifacts_mid 1.0f
+#define fringing_mid 1.0f
+#define std_decoder_hue 0
+
+#define rgb_bits 7 /* half normal range to allow for doubled hires pixels */
+#define gamma_size 32
+
+#include "snes_ntsc_impl.h"
+
+/* 3 input pixels -> 8 composite samples */
+pixel_info_t const snes_ntsc_pixels [alignment_count] = {
+ { PIXEL_OFFSET( -4, -9 ), { 1, 1, .6667f, 0 } },
+ { PIXEL_OFFSET( -2, -7 ), { .3333f, 1, 1, .3333f } },
+ { PIXEL_OFFSET( 0, -5 ), { 0, .6667f, 1, 1 } },
+};
+
+static void merge_kernel_fields( snes_ntsc_rgb_t* io )
+{
+ int n;
+ for ( n = burst_size; n; --n )
+ {
+ snes_ntsc_rgb_t p0 = io [burst_size * 0] + rgb_bias;
+ snes_ntsc_rgb_t p1 = io [burst_size * 1] + rgb_bias;
+ snes_ntsc_rgb_t p2 = io [burst_size * 2] + rgb_bias;
+ /* merge colors without losing precision */
+ io [burst_size * 0] =
+ ((p0 + p1 - ((p0 ^ p1) & snes_ntsc_rgb_builder)) >> 1) - rgb_bias;
+ io [burst_size * 1] =
+ ((p1 + p2 - ((p1 ^ p2) & snes_ntsc_rgb_builder)) >> 1) - rgb_bias;
+ io [burst_size * 2] =
+ ((p2 + p0 - ((p2 ^ p0) & snes_ntsc_rgb_builder)) >> 1) - rgb_bias;
+ ++io;
+ }
+}
+
+static void correct_errors( snes_ntsc_rgb_t color, snes_ntsc_rgb_t* out )
+{
+ int n;
+ for ( n = burst_count; n; --n )
+ {
+ unsigned i;
+ for ( i = 0; i < rgb_kernel_size / 2; i++ )
+ {
+ snes_ntsc_rgb_t error = color -
+ out [i ] - out [(i+12)%14+14] - out [(i+10)%14+28] -
+ out [i + 7] - out [i + 5 +14] - out [i + 3 +28];
+ DISTRIBUTE_ERROR( i+3+28, i+5+14, i+7 );
+ }
+ out += alignment_count * rgb_kernel_size;
+ }
+}
+
+void snes_ntsc_init( snes_ntsc_t* ntsc, snes_ntsc_setup_t const* setup )
+{
+ int merge_fields;
+ int entry;
+ init_t impl;
+ if ( !setup )
+ setup = &snes_ntsc_composite;
+ init( &impl, setup );
+
+ merge_fields = setup->merge_fields;
+ if ( setup->artifacts <= -1 && setup->fringing <= -1 )
+ merge_fields = 1;
+
+ for ( entry = 0; entry < snes_ntsc_palette_size; entry++ )
+ {
+ /* Reduce number of significant bits of source color. Clearing the
+ low bits of R and B were least notictable. Modifying green was too
+ noticeable. */
+ int ir = entry >> 8 & 0x1E;
+ int ig = entry >> 4 & 0x1F;
+ int ib = entry << 1 & 0x1E;
+
+ float rr = impl.to_float [ir];
+ float gg = impl.to_float [ig];
+ float bb = impl.to_float [ib];
+
+ float y, i, q = RGB_TO_YIQ( rr, gg, bb, y, i );
+
+ int r, g, b = YIQ_TO_RGB( y, i, q, impl.to_rgb, int, r, g );
+ snes_ntsc_rgb_t rgb = PACK_RGB( r, g, b );
+
+ snes_ntsc_rgb_t* out = ntsc->table [entry];
+ gen_kernel( &impl, y, i, q, out );
+ if ( merge_fields )
+ merge_kernel_fields( out );
+ correct_errors( rgb, out );
+ }
+}
+
+#ifndef SNES_NTSC_NO_BLITTERS
+
+void snes_ntsc_blit( snes_ntsc_t const* ntsc, SNES_NTSC_IN_T const* input, long in_row_width,
+ int burst_phase, int in_width, int in_height, void* rgb_out, long out_pitch, int first, int last )
+{
+ int chunk_count = (in_width - 1) / snes_ntsc_in_chunk;
+ for ( ; in_height; --in_height )
+ {
+ SNES_NTSC_IN_T const* line_in = input;
+ SNES_NTSC_BEGIN_ROW( ntsc, burst_phase,
+ snes_ntsc_black, snes_ntsc_black, SNES_NTSC_ADJ_IN( *line_in ) );
+ snes_ntsc_out_t* restrict line_out = (snes_ntsc_out_t*) rgb_out;
+ int n;
+ ++line_in;
+
+ for ( n = chunk_count; n; --n )
+ {
+ /* order of input and output pixels must not be altered */
+ SNES_NTSC_COLOR_IN( 0, SNES_NTSC_ADJ_IN( line_in [0] ) );
+ SNES_NTSC_RGB_OUT( 0, line_out [0], SNES_NTSC_OUT_DEPTH );
+ SNES_NTSC_RGB_OUT( 1, line_out [1], SNES_NTSC_OUT_DEPTH );
+
+ SNES_NTSC_COLOR_IN( 1, SNES_NTSC_ADJ_IN( line_in [1] ) );
+ SNES_NTSC_RGB_OUT( 2, line_out [2], SNES_NTSC_OUT_DEPTH );
+ SNES_NTSC_RGB_OUT( 3, line_out [3], SNES_NTSC_OUT_DEPTH );
+
+ SNES_NTSC_COLOR_IN( 2, SNES_NTSC_ADJ_IN( line_in [2] ) );
+ SNES_NTSC_RGB_OUT( 4, line_out [4], SNES_NTSC_OUT_DEPTH );
+ SNES_NTSC_RGB_OUT( 5, line_out [5], SNES_NTSC_OUT_DEPTH );
+ SNES_NTSC_RGB_OUT( 6, line_out [6], SNES_NTSC_OUT_DEPTH );
+
+ line_in += 3;
+ line_out += 7;
+ }
+
+ /* finish final pixels */
+ SNES_NTSC_COLOR_IN( 0, snes_ntsc_black );
+ SNES_NTSC_RGB_OUT( 0, line_out [0], SNES_NTSC_OUT_DEPTH );
+ SNES_NTSC_RGB_OUT( 1, line_out [1], SNES_NTSC_OUT_DEPTH );
+
+ SNES_NTSC_COLOR_IN( 1, snes_ntsc_black );
+ SNES_NTSC_RGB_OUT( 2, line_out [2], SNES_NTSC_OUT_DEPTH );
+ SNES_NTSC_RGB_OUT( 3, line_out [3], SNES_NTSC_OUT_DEPTH );
+
+ SNES_NTSC_COLOR_IN( 2, snes_ntsc_black );
+ SNES_NTSC_RGB_OUT( 4, line_out [4], SNES_NTSC_OUT_DEPTH );
+ SNES_NTSC_RGB_OUT( 5, line_out [5], SNES_NTSC_OUT_DEPTH );
+ SNES_NTSC_RGB_OUT( 6, line_out [6], SNES_NTSC_OUT_DEPTH );
+
+ burst_phase = (burst_phase + 1) % snes_ntsc_burst_count;
+ input += in_row_width;
+ rgb_out = (char*) rgb_out + out_pitch;
+ }
+}
+
+void snes_ntsc_blit_hires( snes_ntsc_t const* ntsc, SNES_NTSC_IN_T const* input, long in_row_width,
+ int burst_phase, int in_width, int in_height, void* rgb_out, long out_pitch, int first, int last )
+{
+ int chunk_count = (in_width - 2) / (snes_ntsc_in_chunk * 2);
+ for ( ; in_height; --in_height )
+ {
+ SNES_NTSC_IN_T const* line_in = input;
+ SNES_NTSC_HIRES_ROW( ntsc, burst_phase,
+ snes_ntsc_black, snes_ntsc_black, snes_ntsc_black,
+ SNES_NTSC_ADJ_IN( line_in [0] ),
+ SNES_NTSC_ADJ_IN( line_in [1] ) );
+ snes_ntsc_out_t* restrict line_out = (snes_ntsc_out_t*) rgb_out;
+ int n;
+ line_in += 2;
+
+ for ( n = chunk_count; n; --n )
+ {
+ /* twice as many input pixels per chunk */
+ SNES_NTSC_COLOR_IN( 0, SNES_NTSC_ADJ_IN( line_in [0] ) );
+ SNES_NTSC_HIRES_OUT( 0, line_out [0], SNES_NTSC_OUT_DEPTH );
+
+ SNES_NTSC_COLOR_IN( 1, SNES_NTSC_ADJ_IN( line_in [1] ) );
+ SNES_NTSC_HIRES_OUT( 1, line_out [1], SNES_NTSC_OUT_DEPTH );
+
+ SNES_NTSC_COLOR_IN( 2, SNES_NTSC_ADJ_IN( line_in [2] ) );
+ SNES_NTSC_HIRES_OUT( 2, line_out [2], SNES_NTSC_OUT_DEPTH );
+
+ SNES_NTSC_COLOR_IN( 3, SNES_NTSC_ADJ_IN( line_in [3] ) );
+ SNES_NTSC_HIRES_OUT( 3, line_out [3], SNES_NTSC_OUT_DEPTH );
+
+ SNES_NTSC_COLOR_IN( 4, SNES_NTSC_ADJ_IN( line_in [4] ) );
+ SNES_NTSC_HIRES_OUT( 4, line_out [4], SNES_NTSC_OUT_DEPTH );
+
+ SNES_NTSC_COLOR_IN( 5, SNES_NTSC_ADJ_IN( line_in [5] ) );
+ SNES_NTSC_HIRES_OUT( 5, line_out [5], SNES_NTSC_OUT_DEPTH );
+ SNES_NTSC_HIRES_OUT( 6, line_out [6], SNES_NTSC_OUT_DEPTH );
+
+ line_in += 6;
+ line_out += 7;
+ }
+
+ SNES_NTSC_COLOR_IN( 0, snes_ntsc_black );
+ SNES_NTSC_HIRES_OUT( 0, line_out [0], SNES_NTSC_OUT_DEPTH );
+
+ SNES_NTSC_COLOR_IN( 1, snes_ntsc_black );
+ SNES_NTSC_HIRES_OUT( 1, line_out [1], SNES_NTSC_OUT_DEPTH );
+
+ SNES_NTSC_COLOR_IN( 2, snes_ntsc_black );
+ SNES_NTSC_HIRES_OUT( 2, line_out [2], SNES_NTSC_OUT_DEPTH );
+
+ SNES_NTSC_COLOR_IN( 3, snes_ntsc_black );
+ SNES_NTSC_HIRES_OUT( 3, line_out [3], SNES_NTSC_OUT_DEPTH );
+
+ SNES_NTSC_COLOR_IN( 4, snes_ntsc_black );
+ SNES_NTSC_HIRES_OUT( 4, line_out [4], SNES_NTSC_OUT_DEPTH );
+
+ SNES_NTSC_COLOR_IN( 5, snes_ntsc_black );
+ SNES_NTSC_HIRES_OUT( 5, line_out [5], SNES_NTSC_OUT_DEPTH );
+ SNES_NTSC_HIRES_OUT( 6, line_out [6], SNES_NTSC_OUT_DEPTH );
+
+ burst_phase = (burst_phase + 1) % snes_ntsc_burst_count;
+ input += in_row_width;
+ rgb_out = (char*) rgb_out + out_pitch;
+ }
+}
+
+#endif
diff --git a/gfx/filters/snes_ntsc/snes_ntsc.h b/gfx/filters/snes_ntsc/snes_ntsc.h
new file mode 100755
index 0000000000..82205e0115
--- /dev/null
+++ b/gfx/filters/snes_ntsc/snes_ntsc.h
@@ -0,0 +1,207 @@
+/* SNES NTSC video filter */
+
+/* snes_ntsc 0.2.2 */
+#ifndef SNES_NTSC_H
+#define SNES_NTSC_H
+
+#include "snes_ntsc_config.h"
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/* Image parameters, ranging from -1.0 to 1.0. Actual internal values shown
+in parenthesis and should remain fairly stable in future versions. */
+typedef struct snes_ntsc_setup_t
+{
+ /* Basic parameters */
+ double hue; /* -1 = -180 degrees +1 = +180 degrees */
+ double saturation; /* -1 = grayscale (0.0) +1 = oversaturated colors (2.0) */
+ double contrast; /* -1 = dark (0.5) +1 = light (1.5) */
+ double brightness; /* -1 = dark (0.5) +1 = light (1.5) */
+ double sharpness; /* edge contrast enhancement/blurring */
+
+ /* Advanced parameters */
+ double gamma; /* -1 = dark (1.5) +1 = light (0.5) */
+ double resolution; /* image resolution */
+ double artifacts; /* artifacts caused by color changes */
+ double fringing; /* color artifacts caused by brightness changes */
+ double bleed; /* color bleed (color resolution reduction) */
+ int merge_fields; /* if 1, merges even and odd fields together to reduce flicker */
+ float const* decoder_matrix; /* optional RGB decoder matrix, 6 elements */
+
+ unsigned long const* bsnes_colortbl; /* undocumented; set to 0 */
+} snes_ntsc_setup_t;
+
+/* Video format presets */
+extern snes_ntsc_setup_t const snes_ntsc_composite; /* color bleeding + artifacts */
+extern snes_ntsc_setup_t const snes_ntsc_svideo; /* color bleeding only */
+extern snes_ntsc_setup_t const snes_ntsc_rgb; /* crisp image */
+extern snes_ntsc_setup_t const snes_ntsc_monochrome;/* desaturated + artifacts */
+
+/* Initializes and adjusts parameters. Can be called multiple times on the same
+snes_ntsc_t object. Can pass NULL for either parameter. */
+typedef struct snes_ntsc_t snes_ntsc_t;
+void snes_ntsc_init( snes_ntsc_t* ntsc, snes_ntsc_setup_t const* setup );
+
+/* Filters one or more rows of pixels. Input pixel format is set by SNES_NTSC_IN_FORMAT
+and output RGB depth is set by SNES_NTSC_OUT_DEPTH. Both default to 16-bit RGB.
+In_row_width is the number of pixels to get to the next input row. Out_pitch
+is the number of *bytes* to get to the next output row. */
+void snes_ntsc_blit( snes_ntsc_t const* ntsc, SNES_NTSC_IN_T const* input,
+ long in_row_width, int burst_phase, int in_width, int in_height,
+ void* rgb_out, long out_pitch, int first, int last);
+
+void snes_ntsc_blit_hires( snes_ntsc_t const* ntsc, SNES_NTSC_IN_T const* input,
+ long in_row_width, int burst_phase, int in_width, int in_height,
+ void* rgb_out, long out_pitch, int first, int last);
+
+/* Number of output pixels written by low-res blitter for given input width. Width
+might be rounded down slightly; use SNES_NTSC_IN_WIDTH() on result to find rounded
+value. Guaranteed not to round 256 down at all. */
+#define SNES_NTSC_OUT_WIDTH( in_width ) \
+ ((((in_width) - 1) / snes_ntsc_in_chunk + 1) * snes_ntsc_out_chunk)
+
+/* Number of low-res input pixels that will fit within given output width. Might be
+rounded down slightly; use SNES_NTSC_OUT_WIDTH() on result to find rounded
+value. */
+#define SNES_NTSC_IN_WIDTH( out_width ) \
+ (((out_width) / snes_ntsc_out_chunk - 1) * snes_ntsc_in_chunk + 1)
+
+
+/* Interface for user-defined custom blitters */
+
+enum { snes_ntsc_in_chunk = 3 }; /* number of input pixels read per chunk */
+enum { snes_ntsc_out_chunk = 7 }; /* number of output pixels generated per chunk */
+enum { snes_ntsc_black = 0 }; /* palette index for black */
+enum { snes_ntsc_burst_count = 3 }; /* burst phase cycles through 0, 1, and 2 */
+
+/* Begins outputting row and starts three pixels. First pixel will be cut off a bit.
+Use snes_ntsc_black for unused pixels. Declares variables, so must be before first
+statement in a block (unless you're using C++). */
+#define SNES_NTSC_BEGIN_ROW( ntsc, burst, pixel0, pixel1, pixel2 ) \
+ char const* ktable = \
+ (char const*) (ntsc)->table + burst * (snes_ntsc_burst_size * sizeof (snes_ntsc_rgb_t));\
+ SNES_NTSC_BEGIN_ROW_6_( pixel0, pixel1, pixel2, SNES_NTSC_IN_FORMAT, ktable )
+
+/* Begins input pixel */
+#define SNES_NTSC_COLOR_IN( index, color ) \
+ SNES_NTSC_COLOR_IN_( index, color, SNES_NTSC_IN_FORMAT, ktable )
+
+/* Generates output pixel. Bits can be 24, 16, 15, 14, 32 (treated as 24), or 0:
+24: RRRRRRRR GGGGGGGG BBBBBBBB (8-8-8 RGB)
+16: RRRRRGGG GGGBBBBB (5-6-5 RGB)
+15: RRRRRGG GGGBBBBB (5-5-5 RGB)
+14: BBBBBGG GGGRRRRR (5-5-5 BGR, native SNES format)
+ 0: xxxRRRRR RRRxxGGG GGGGGxxB BBBBBBBx (native internal format; x = junk bits) */
+#define SNES_NTSC_RGB_OUT( index, rgb_out, bits ) \
+ SNES_NTSC_RGB_OUT_14_( index, rgb_out, bits, 1 )
+
+/* Hires equivalents */
+#define SNES_NTSC_HIRES_ROW( ntsc, burst, pixel1, pixel2, pixel3, pixel4, pixel5 ) \
+ char const* ktable = \
+ (char const*) (ntsc)->table + burst * (snes_ntsc_burst_size * sizeof (snes_ntsc_rgb_t));\
+ unsigned const snes_ntsc_pixel1_ = (pixel1);\
+ snes_ntsc_rgb_t const* kernel1 = SNES_NTSC_IN_FORMAT( ktable, snes_ntsc_pixel1_ );\
+ unsigned const snes_ntsc_pixel2_ = (pixel2);\
+ snes_ntsc_rgb_t const* kernel2 = SNES_NTSC_IN_FORMAT( ktable, snes_ntsc_pixel2_ );\
+ unsigned const snes_ntsc_pixel3_ = (pixel3);\
+ snes_ntsc_rgb_t const* kernel3 = SNES_NTSC_IN_FORMAT( ktable, snes_ntsc_pixel3_ );\
+ unsigned const snes_ntsc_pixel4_ = (pixel4);\
+ snes_ntsc_rgb_t const* kernel4 = SNES_NTSC_IN_FORMAT( ktable, snes_ntsc_pixel4_ );\
+ unsigned const snes_ntsc_pixel5_ = (pixel5);\
+ snes_ntsc_rgb_t const* kernel5 = SNES_NTSC_IN_FORMAT( ktable, snes_ntsc_pixel5_ );\
+ snes_ntsc_rgb_t const* kernel0 = kernel1;\
+ snes_ntsc_rgb_t const* kernelx0;\
+ snes_ntsc_rgb_t const* kernelx1 = kernel1;\
+ snes_ntsc_rgb_t const* kernelx2 = kernel1;\
+ snes_ntsc_rgb_t const* kernelx3 = kernel1;\
+ snes_ntsc_rgb_t const* kernelx4 = kernel1;\
+ snes_ntsc_rgb_t const* kernelx5 = kernel1
+
+#define SNES_NTSC_HIRES_OUT( x, rgb_out, bits ) {\
+ snes_ntsc_rgb_t raw_ =\
+ kernel0 [ x ] + kernel2 [(x+5)%7+14] + kernel4 [(x+3)%7+28] +\
+ kernelx0 [(x+7)%7+7] + kernelx2 [(x+5)%7+21] + kernelx4 [(x+3)%7+35] +\
+ kernel1 [(x+6)%7 ] + kernel3 [(x+4)%7+14] + kernel5 [(x+2)%7+28] +\
+ kernelx1 [(x+6)%7+7] + kernelx3 [(x+4)%7+21] + kernelx5 [(x+2)%7+35];\
+ SNES_NTSC_CLAMP_( raw_, 0 );\
+ SNES_NTSC_RGB_OUT_( rgb_out, (bits), 0 );\
+}
+
+
+/* private */
+enum { snes_ntsc_entry_size = 128 };
+enum { snes_ntsc_palette_size = 0x2000 };
+typedef unsigned long snes_ntsc_rgb_t;
+struct snes_ntsc_t {
+ snes_ntsc_rgb_t table [snes_ntsc_palette_size] [snes_ntsc_entry_size];
+};
+enum { snes_ntsc_burst_size = snes_ntsc_entry_size / snes_ntsc_burst_count };
+
+#define SNES_NTSC_RGB16( ktable, n ) \
+ (snes_ntsc_rgb_t const*) (ktable + ((n & 0x001E) | (n >> 1 & 0x03E0) | (n >> 2 & 0x3C00)) * \
+ (snes_ntsc_entry_size / 2 * sizeof (snes_ntsc_rgb_t)))
+
+#define SNES_NTSC_BGR15( ktable, n ) \
+ (snes_ntsc_rgb_t const*) (ktable + ((n << 9 & 0x3C00) | (n & 0x03E0) | (n >> 10 & 0x001E)) * \
+ (snes_ntsc_entry_size / 2 * sizeof (snes_ntsc_rgb_t)))
+
+/* common 3->7 ntsc macros */
+#define SNES_NTSC_BEGIN_ROW_6_( pixel0, pixel1, pixel2, ENTRY, table ) \
+ unsigned const snes_ntsc_pixel0_ = (pixel0);\
+ snes_ntsc_rgb_t const* kernel0 = ENTRY( table, snes_ntsc_pixel0_ );\
+ unsigned const snes_ntsc_pixel1_ = (pixel1);\
+ snes_ntsc_rgb_t const* kernel1 = ENTRY( table, snes_ntsc_pixel1_ );\
+ unsigned const snes_ntsc_pixel2_ = (pixel2);\
+ snes_ntsc_rgb_t const* kernel2 = ENTRY( table, snes_ntsc_pixel2_ );\
+ snes_ntsc_rgb_t const* kernelx0;\
+ snes_ntsc_rgb_t const* kernelx1 = kernel0;\
+ snes_ntsc_rgb_t const* kernelx2 = kernel0
+
+#define SNES_NTSC_RGB_OUT_14_( x, rgb_out, bits, shift ) {\
+ snes_ntsc_rgb_t raw_ =\
+ kernel0 [x ] + kernel1 [(x+12)%7+14] + kernel2 [(x+10)%7+28] +\
+ kernelx0 [(x+7)%14] + kernelx1 [(x+ 5)%7+21] + kernelx2 [(x+ 3)%7+35];\
+ SNES_NTSC_CLAMP_( raw_, shift );\
+ SNES_NTSC_RGB_OUT_( rgb_out, bits, shift );\
+}
+
+/* common ntsc macros */
+#define snes_ntsc_rgb_builder ((1L << 21) | (1 << 11) | (1 << 1))
+#define snes_ntsc_clamp_mask (snes_ntsc_rgb_builder * 3 / 2)
+#define snes_ntsc_clamp_add (snes_ntsc_rgb_builder * 0x101)
+#define SNES_NTSC_CLAMP_( io, shift ) {\
+ snes_ntsc_rgb_t sub = (io) >> (9-(shift)) & snes_ntsc_clamp_mask;\
+ snes_ntsc_rgb_t clamp = snes_ntsc_clamp_add - sub;\
+ io |= clamp;\
+ clamp -= sub;\
+ io &= clamp;\
+}
+
+#define SNES_NTSC_COLOR_IN_( index, color, ENTRY, table ) {\
+ unsigned color_;\
+ kernelx##index = kernel##index;\
+ kernel##index = (color_ = (color), ENTRY( table, color_ ));\
+}
+
+/* x is always zero except in snes_ntsc library */
+/* original routine */
+#define SNES_NTSC_RGB_OUT_( rgb_out, bits, x ) {\
+ if ( bits == 16 )\
+ rgb_out = (raw_>>(13-x)& 0xF800)|(raw_>>(8-x)&0x07E0)|(raw_>>(4-x)&0x001F);\
+ if ( bits == 24 || bits == 32 )\
+ rgb_out = (raw_>>(5-x)&0xFF0000)|(raw_>>(3-x)&0xFF00)|(raw_>>(1-x)&0xFF);\
+ if ( bits == 15 )\
+ rgb_out = (raw_>>(14-x)& 0x7C00)|(raw_>>(9-x)&0x03E0)|(raw_>>(4-x)&0x001F);\
+ if ( bits == 14 )\
+ rgb_out = (raw_>>(24-x)& 0x001F)|(raw_>>(9-x)&0x03E0)|(raw_<<(6+x)&0x7C00);\
+ if ( bits == 0 )\
+ rgb_out = raw_ << x;\
+}
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif
diff --git a/gfx/filters/snes_ntsc/snes_ntsc_config.h b/gfx/filters/snes_ntsc/snes_ntsc_config.h
new file mode 100755
index 0000000000..790c44c278
--- /dev/null
+++ b/gfx/filters/snes_ntsc/snes_ntsc_config.h
@@ -0,0 +1,26 @@
+/* Configure library by modifying this file */
+
+#ifndef SNES_NTSC_CONFIG_H
+#define SNES_NTSC_CONFIG_H
+
+/* Format of source pixels */
+/* #define SNES_NTSC_IN_FORMAT SNES_NTSC_RGB16 */
+#define SNES_NTSC_IN_FORMAT SNES_NTSC_RGB16
+
+/* The following affect the built-in blitter only; a custom blitter can
+handle things however it wants. */
+
+/* Bits per pixel of output. Can be 15, 16, 32, or 24 (same as 32). */
+#define SNES_NTSC_OUT_DEPTH 16
+
+/* Type of input pixel values */
+#define SNES_NTSC_IN_T unsigned short
+
+/* Each raw pixel input value is passed through this. You might want to mask
+the pixel index if you use the high bits as flags, etc. */
+#define SNES_NTSC_ADJ_IN( in ) in
+
+/* For each pixel, this is the basic operation:
+output_color = SNES_NTSC_ADJ_IN( SNES_NTSC_IN_T ) */
+
+#endif
diff --git a/gfx/filters/snes_ntsc/snes_ntsc_impl.h b/gfx/filters/snes_ntsc/snes_ntsc_impl.h
new file mode 100755
index 0000000000..1d7adc78b8
--- /dev/null
+++ b/gfx/filters/snes_ntsc/snes_ntsc_impl.h
@@ -0,0 +1,439 @@
+/* snes_ntsc 0.2.2. http://www.slack.net/~ant/ */
+
+/* Common implementation of NTSC filters */
+
+#include
+#include
+
+/* Copyright (C) 2006 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#define DISABLE_CORRECTION 0
+
+#undef PI
+#define PI 3.14159265358979323846f
+
+#ifndef LUMA_CUTOFF
+ #define LUMA_CUTOFF 0.20
+#endif
+#ifndef gamma_size
+ #define gamma_size 1
+#endif
+#ifndef rgb_bits
+ #define rgb_bits 8
+#endif
+#ifndef artifacts_max
+ #define artifacts_max (artifacts_mid * 1.5f)
+#endif
+#ifndef fringing_max
+ #define fringing_max (fringing_mid * 2)
+#endif
+#ifndef STD_HUE_CONDITION
+ #define STD_HUE_CONDITION( setup ) 1
+#endif
+
+#define ext_decoder_hue (std_decoder_hue + 15)
+#define rgb_unit (1 << rgb_bits)
+#define rgb_offset (rgb_unit * 2 + 0.5f)
+
+enum { burst_size = snes_ntsc_entry_size / burst_count };
+enum { kernel_half = 16 };
+enum { kernel_size = kernel_half * 2 + 1 };
+
+typedef struct init_t
+{
+ float to_rgb [burst_count * 6];
+ float to_float [gamma_size];
+ float contrast;
+ float brightness;
+ float artifacts;
+ float fringing;
+ float kernel [rescale_out * kernel_size * 2];
+} init_t;
+
+#define ROTATE_IQ( i, q, sin_b, cos_b ) {\
+ float t;\
+ t = i * cos_b - q * sin_b;\
+ q = i * sin_b + q * cos_b;\
+ i = t;\
+}
+
+static void init_filters( init_t* impl, snes_ntsc_setup_t const* setup )
+{
+#if rescale_out > 1
+ float kernels [kernel_size * 2];
+#else
+ float* const kernels = impl->kernel;
+#endif
+
+ /* generate luma (y) filter using sinc kernel */
+ {
+ /* sinc with rolloff (dsf) */
+ float const rolloff = 1 + (float) setup->sharpness * (float) 0.032;
+ float const maxh = 32;
+ float const pow_a_n = (float) pow( rolloff, maxh );
+ float sum;
+ int i;
+ /* quadratic mapping to reduce negative (blurring) range */
+ float to_angle = (float) setup->resolution + 1;
+ to_angle = PI / maxh * (float) LUMA_CUTOFF * (to_angle * to_angle + 1);
+
+ kernels [kernel_size * 3 / 2] = maxh; /* default center value */
+ for ( i = 0; i < kernel_half * 2 + 1; i++ )
+ {
+ int x = i - kernel_half;
+ float angle = x * to_angle;
+ /* instability occurs at center point with rolloff very close to 1.0 */
+ if ( x || pow_a_n > (float) 1.056 || pow_a_n < (float) 0.981 )
+ {
+ float rolloff_cos_a = rolloff * (float) cos( angle );
+ float num = 1 - rolloff_cos_a -
+ pow_a_n * (float) cos( maxh * angle ) +
+ pow_a_n * rolloff * (float) cos( (maxh - 1) * angle );
+ float den = 1 - rolloff_cos_a - rolloff_cos_a + rolloff * rolloff;
+ float dsf = num / den;
+ kernels [kernel_size * 3 / 2 - kernel_half + i] = dsf - (float) 0.5;
+ }
+ }
+
+ /* apply blackman window and find sum */
+ sum = 0;
+ for ( i = 0; i < kernel_half * 2 + 1; i++ )
+ {
+ float x = PI * 2 / (kernel_half * 2) * i;
+ float blackman = 0.42f - 0.5f * (float) cos( x ) + 0.08f * (float) cos( x * 2 );
+ sum += (kernels [kernel_size * 3 / 2 - kernel_half + i] *= blackman);
+ }
+
+ /* normalize kernel */
+ sum = 1.0f / sum;
+ for ( i = 0; i < kernel_half * 2 + 1; i++ )
+ {
+ int x = kernel_size * 3 / 2 - kernel_half + i;
+ kernels [x] *= sum;
+ assert( kernels [x] == kernels [x] ); /* catch numerical instability */
+ }
+ }
+
+ /* generate chroma (iq) filter using gaussian kernel */
+ {
+ float const cutoff_factor = -0.03125f;
+ float cutoff = (float) setup->bleed;
+ int i;
+
+ if ( cutoff < 0 )
+ {
+ /* keep extreme value accessible only near upper end of scale (1.0) */
+ cutoff *= cutoff;
+ cutoff *= cutoff;
+ cutoff *= cutoff;
+ cutoff *= -30.0f / 0.65f;
+ }
+ cutoff = cutoff_factor - 0.65f * cutoff_factor * cutoff;
+
+ for ( i = -kernel_half; i <= kernel_half; i++ )
+ kernels [kernel_size / 2 + i] = (float) exp( i * i * cutoff );
+
+ /* normalize even and odd phases separately */
+ for ( i = 0; i < 2; i++ )
+ {
+ float sum = 0;
+ int x;
+ for ( x = i; x < kernel_size; x += 2 )
+ sum += kernels [x];
+
+ sum = 1.0f / sum;
+ for ( x = i; x < kernel_size; x += 2 )
+ {
+ kernels [x] *= sum;
+ assert( kernels [x] == kernels [x] ); /* catch numerical instability */
+ }
+ }
+ }
+
+ /*
+ printf( "luma:\n" );
+ for ( i = kernel_size; i < kernel_size * 2; i++ )
+ printf( "%f\n", kernels [i] );
+ printf( "chroma:\n" );
+ for ( i = 0; i < kernel_size; i++ )
+ printf( "%f\n", kernels [i] );
+ */
+
+ /* generate linear rescale kernels */
+ #if rescale_out > 1
+ {
+ float weight = 1.0f;
+ float* out = impl->kernel;
+ int n = rescale_out;
+ do
+ {
+ float remain = 0;
+ int i;
+ weight -= 1.0f / rescale_in;
+ for ( i = 0; i < kernel_size * 2; i++ )
+ {
+ float cur = kernels [i];
+ float m = cur * weight;
+ *out++ = m + remain;
+ remain = cur - m;
+ }
+ }
+ while ( --n );
+ }
+ #endif
+}
+
+static float const default_decoder [6] =
+ { 0.956f, 0.621f, -0.272f, -0.647f, -1.105f, 1.702f };
+
+static void init( init_t* impl, snes_ntsc_setup_t const* setup )
+{
+ impl->brightness = (float) setup->brightness * (0.5f * rgb_unit) + rgb_offset;
+ impl->contrast = (float) setup->contrast * (0.5f * rgb_unit) + rgb_unit;
+ #ifdef default_palette_contrast
+ if ( !setup->palette )
+ impl->contrast *= default_palette_contrast;
+ #endif
+
+ impl->artifacts = (float) setup->artifacts;
+ if ( impl->artifacts > 0 )
+ impl->artifacts *= artifacts_max - artifacts_mid;
+ impl->artifacts = impl->artifacts * artifacts_mid + artifacts_mid;
+
+ impl->fringing = (float) setup->fringing;
+ if ( impl->fringing > 0 )
+ impl->fringing *= fringing_max - fringing_mid;
+ impl->fringing = impl->fringing * fringing_mid + fringing_mid;
+
+ init_filters( impl, setup );
+
+ /* generate gamma table */
+ if ( gamma_size > 1 )
+ {
+ float const to_float = 1.0f / (gamma_size - (gamma_size > 1));
+ float const gamma = 1.1333f - (float) setup->gamma * 0.5f;
+ /* match common PC's 2.2 gamma to TV's 2.65 gamma */
+ int i;
+ for ( i = 0; i < gamma_size; i++ )
+ impl->to_float [i] =
+ (float) pow( i * to_float, gamma ) * impl->contrast + impl->brightness;
+ }
+
+ /* setup decoder matricies */
+ {
+ float hue = (float) setup->hue * PI + PI / 180 * ext_decoder_hue;
+ float sat = (float) setup->saturation + 1;
+ float const* decoder = setup->decoder_matrix;
+ if ( !decoder )
+ {
+ decoder = default_decoder;
+ if ( STD_HUE_CONDITION( setup ) )
+ hue += PI / 180 * (std_decoder_hue - ext_decoder_hue);
+ }
+
+ {
+ float s = (float) sin( hue ) * sat;
+ float c = (float) cos( hue ) * sat;
+ float* out = impl->to_rgb;
+ int n;
+
+ n = burst_count;
+ do
+ {
+ float const* in = decoder;
+ int n = 3;
+ do
+ {
+ float i = *in++;
+ float q = *in++;
+ *out++ = i * c - q * s;
+ *out++ = i * s + q * c;
+ }
+ while ( --n );
+ if ( burst_count <= 1 )
+ break;
+ ROTATE_IQ( s, c, 0.866025f, -0.5f ); /* +120 degrees */
+ }
+ while ( --n );
+ }
+ }
+}
+
+/* kernel generation */
+
+#define RGB_TO_YIQ( r, g, b, y, i ) (\
+ (y = (r) * 0.299f + (g) * 0.587f + (b) * 0.114f),\
+ (i = (r) * 0.596f - (g) * 0.275f - (b) * 0.321f),\
+ ((r) * 0.212f - (g) * 0.523f + (b) * 0.311f)\
+)
+
+#define YIQ_TO_RGB( y, i, q, to_rgb, type, r, g ) (\
+ r = (type) (y + to_rgb [0] * i + to_rgb [1] * q),\
+ g = (type) (y + to_rgb [2] * i + to_rgb [3] * q),\
+ (type) (y + to_rgb [4] * i + to_rgb [5] * q)\
+)
+
+#define PACK_RGB( r, g, b ) ((r) << 21 | (g) << 11 | (b) << 1)
+
+enum { rgb_kernel_size = burst_size / alignment_count };
+enum { rgb_bias = rgb_unit * 2 * snes_ntsc_rgb_builder };
+
+typedef struct pixel_info_t
+{
+ int offset;
+ float negate;
+ float kernel [4];
+} pixel_info_t;
+
+#if rescale_in > 1
+ #define PIXEL_OFFSET_( ntsc, scaled ) \
+ (kernel_size / 2 + ntsc + (scaled != 0) + (rescale_out - scaled) % rescale_out + \
+ (kernel_size * 2 * scaled))
+
+ #define PIXEL_OFFSET( ntsc, scaled ) \
+ PIXEL_OFFSET_( ((ntsc) - (scaled) / rescale_out * rescale_in),\
+ (((scaled) + rescale_out * 10) % rescale_out) ),\
+ (1.0f - (((ntsc) + 100) & 2))
+#else
+ #define PIXEL_OFFSET( ntsc, scaled ) \
+ (kernel_size / 2 + (ntsc) - (scaled)),\
+ (1.0f - (((ntsc) + 100) & 2))
+#endif
+
+extern pixel_info_t const snes_ntsc_pixels [alignment_count];
+
+/* Generate pixel at all burst phases and column alignments */
+static void gen_kernel( init_t* impl, float y, float i, float q, snes_ntsc_rgb_t* out )
+{
+ /* generate for each scanline burst phase */
+ float const* to_rgb = impl->to_rgb;
+ int burst_remain = burst_count;
+ y -= rgb_offset;
+ do
+ {
+ /* Encode yiq into *two* composite signals (to allow control over artifacting).
+ Convolve these with kernels which: filter respective components, apply
+ sharpening, and rescale horizontally. Convert resulting yiq to rgb and pack
+ into integer. Based on algorithm by NewRisingSun. */
+ pixel_info_t const* pixel = snes_ntsc_pixels;
+ int alignment_remain = alignment_count;
+ do
+ {
+ /* negate is -1 when composite starts at odd multiple of 2 */
+ float const yy = y * impl->fringing * pixel->negate;
+ float const ic0 = (i + yy) * pixel->kernel [0];
+ float const qc1 = (q + yy) * pixel->kernel [1];
+ float const ic2 = (i - yy) * pixel->kernel [2];
+ float const qc3 = (q - yy) * pixel->kernel [3];
+
+ float const factor = impl->artifacts * pixel->negate;
+ float const ii = i * factor;
+ float const yc0 = (y + ii) * pixel->kernel [0];
+ float const yc2 = (y - ii) * pixel->kernel [2];
+
+ float const qq = q * factor;
+ float const yc1 = (y + qq) * pixel->kernel [1];
+ float const yc3 = (y - qq) * pixel->kernel [3];
+
+ float const* k = &impl->kernel [pixel->offset];
+ int n;
+ ++pixel;
+ for ( n = rgb_kernel_size; n; --n )
+ {
+ float i = k[0]*ic0 + k[2]*ic2;
+ float q = k[1]*qc1 + k[3]*qc3;
+ float y = k[kernel_size+0]*yc0 + k[kernel_size+1]*yc1 +
+ k[kernel_size+2]*yc2 + k[kernel_size+3]*yc3 + rgb_offset;
+ if ( rescale_out <= 1 )
+ k--;
+ else if ( k < &impl->kernel [kernel_size * 2 * (rescale_out - 1)] )
+ k += kernel_size * 2 - 1;
+ else
+ k -= kernel_size * 2 * (rescale_out - 1) + 2;
+ {
+ int r, g, b = YIQ_TO_RGB( y, i, q, to_rgb, int, r, g );
+ *out++ = PACK_RGB( r, g, b ) - rgb_bias;
+ }
+ }
+ }
+ while ( alignment_count > 1 && --alignment_remain );
+
+ if ( burst_count <= 1 )
+ break;
+
+ to_rgb += 6;
+
+ ROTATE_IQ( i, q, -0.866025f, -0.5f ); /* -120 degrees */
+ }
+ while ( --burst_remain );
+}
+
+static void correct_errors( snes_ntsc_rgb_t color, snes_ntsc_rgb_t* out );
+
+#if DISABLE_CORRECTION
+ #define CORRECT_ERROR( a ) { out [i] += rgb_bias; }
+ #define DISTRIBUTE_ERROR( a, b, c ) { out [i] += rgb_bias; }
+#else
+ #define CORRECT_ERROR( a ) { out [a] += error; }
+ #define DISTRIBUTE_ERROR( a, b, c ) {\
+ snes_ntsc_rgb_t fourth = (error + 2 * snes_ntsc_rgb_builder) >> 2;\
+ fourth &= (rgb_bias >> 1) - snes_ntsc_rgb_builder;\
+ fourth -= rgb_bias >> 2;\
+ out [a] += fourth;\
+ out [b] += fourth;\
+ out [c] += fourth;\
+ out [i] += error - (fourth * 3);\
+ }
+#endif
+
+#define RGB_PALETTE_OUT( rgb, out_ )\
+{\
+ unsigned char* out = (out_);\
+ snes_ntsc_rgb_t clamped = (rgb);\
+ SNES_NTSC_CLAMP_( clamped, (8 - rgb_bits) );\
+ out [0] = (unsigned char) (clamped >> 21);\
+ out [1] = (unsigned char) (clamped >> 11);\
+ out [2] = (unsigned char) (clamped >> 1);\
+}
+
+/* blitter related */
+
+#ifndef restrict
+ #if defined (__GNUC__)
+ #define restrict __restrict__
+ #elif defined (_MSC_VER) && _MSC_VER > 1300
+ #define restrict __restrict
+ #else
+ /* no support for restricted pointers */
+ #define restrict
+ #endif
+#endif
+
+#include
+
+#if SNES_NTSC_OUT_DEPTH <= 16
+ #if USHRT_MAX == 0xFFFF
+ typedef unsigned short snes_ntsc_out_t;
+ #else
+ #error "Need 16-bit int type"
+ #endif
+
+#else
+ #if UINT_MAX == 0xFFFFFFFF
+ typedef unsigned int snes_ntsc_out_t;
+ #elif ULONG_MAX == 0xFFFFFFFF
+ typedef unsigned long snes_ntsc_out_t;
+ #else
+ #error "Need 32-bit int type"
+ #endif
+
+#endif