1 /* SNES NTSC video filter */ 2 3 /* snes_ntsc 0.2.2 */ 4 #ifndef SNES_NTSC_H 5 #define SNES_NTSC_H 6 7 #include "snes_ntsc_config.h" 8 9 #ifdef __cplusplus 10 extern "C" { 11 #endif 12 13 /* Image parameters, ranging from -1.0 to 1.0. Actual internal values shown 14 in parenthesis and should remain fairly stable in future versions. */ 15 typedef struct snes_ntsc_setup_t 16 { 17 /* Basic parameters */ 18 double hue; /* -1 = -180 degrees +1 = +180 degrees */ 19 double saturation; /* -1 = grayscale (0.0) +1 = oversaturated colors (2.0) */ 20 double contrast; /* -1 = dark (0.5) +1 = light (1.5) */ 21 double brightness; /* -1 = dark (0.5) +1 = light (1.5) */ 22 double sharpness; /* edge contrast enhancement/blurring */ 23 24 /* Advanced parameters */ 25 double gamma; /* -1 = dark (1.5) +1 = light (0.5) */ 26 double resolution; /* image resolution */ 27 double artifacts; /* artifacts caused by color changes */ 28 double fringing; /* color artifacts caused by brightness changes */ 29 double bleed; /* color bleed (color resolution reduction) */ 30 int merge_fields; /* if 1, merges even and odd fields together to reduce flicker */ 31 float const* decoder_matrix; /* optional RGB decoder matrix, 6 elements */ 32 33 unsigned long const* bsnes_colortbl; /* undocumented; set to 0 */ 34 } snes_ntsc_setup_t; 35 36 /* Video format presets */ 37 extern snes_ntsc_setup_t const snes_ntsc_composite; /* color bleeding + artifacts */ 38 extern snes_ntsc_setup_t const snes_ntsc_svideo; /* color bleeding only */ 39 extern snes_ntsc_setup_t const snes_ntsc_rgb; /* crisp image */ 40 extern snes_ntsc_setup_t const snes_ntsc_monochrome;/* desaturated + artifacts */ 41 42 /* Initializes and adjusts parameters. Can be called multiple times on the same 43 snes_ntsc_t object. Can pass NULL for either parameter. */ 44 typedef struct snes_ntsc_t snes_ntsc_t; 45 void snes_ntsc_init( snes_ntsc_t* ntsc, snes_ntsc_setup_t const* setup ); 46 47 /* Filters one or more rows of pixels. Input pixel format is set by SNES_NTSC_IN_FORMAT 48 and output RGB depth is set by SNES_NTSC_OUT_DEPTH. Both default to 16-bit RGB. 49 In_row_width is the number of pixels to get to the next input row. Out_pitch 50 is the number of *bytes* to get to the next output row. */ 51 void snes_ntsc_blit( snes_ntsc_t const* ntsc, SNES_NTSC_IN_T const* input, 52 long in_row_width, int burst_phase, int in_width, int in_height, 53 void* rgb_out, long out_pitch ); 54 55 void snes_ntsc_blit_hires( snes_ntsc_t const* ntsc, SNES_NTSC_IN_T const* input, 56 long in_row_width, int burst_phase, int in_width, int in_height, 57 void* rgb_out, long out_pitch ); 58 59 /* ARCAN hack to share update code with libretro and hijack lib implementation */ 60 void snes_ntsc_update_setup( snes_ntsc_t* ntsc, snes_ntsc_setup_t* dst, int group, float v1, float v2, float v3); 61 62 /* Number of output pixels written by low-res blitter for given input width. Width 63 might be rounded down slightly; use SNES_NTSC_IN_WIDTH() on result to find rounded 64 value. Guaranteed not to round 256 down at all. */ 65 #define SNES_NTSC_OUT_WIDTH( in_width ) \ 66 ((((in_width) - 1) / snes_ntsc_in_chunk + 1) * snes_ntsc_out_chunk) 67 68 /* Number of low-res input pixels that will fit within given output width. Might be 69 rounded down slightly; use SNES_NTSC_OUT_WIDTH() on result to find rounded 70 value. */ 71 #define SNES_NTSC_IN_WIDTH( out_width ) \ 72 (((out_width) / snes_ntsc_out_chunk - 1) * snes_ntsc_in_chunk + 1) 73 74 75 /* Interface for user-defined custom blitters */ 76 77 enum { snes_ntsc_in_chunk = 3 }; /* number of input pixels read per chunk */ 78 enum { snes_ntsc_out_chunk = 7 }; /* number of output pixels generated per chunk */ 79 enum { snes_ntsc_black = 0 }; /* palette index for black */ 80 enum { snes_ntsc_burst_count = 3 }; /* burst phase cycles through 0, 1, and 2 */ 81 82 /* Begins outputting row and starts three pixels. First pixel will be cut off a bit. 83 Use snes_ntsc_black for unused pixels. Declares variables, so must be before first 84 statement in a block (unless you're using C++). */ 85 #define SNES_NTSC_BEGIN_ROW( ntsc, burst, pixel0, pixel1, pixel2 ) \ 86 char const* ktable = \ 87 (char const*) (ntsc)->table + burst * (snes_ntsc_burst_size * sizeof (snes_ntsc_rgb_t));\ 88 SNES_NTSC_BEGIN_ROW_6_( pixel0, pixel1, pixel2, SNES_NTSC_IN_FORMAT, ktable ) 89 90 /* Begins input pixel */ 91 #define SNES_NTSC_COLOR_IN( index, color ) \ 92 SNES_NTSC_COLOR_IN_( index, color, SNES_NTSC_IN_FORMAT, ktable ) 93 94 /* Generates output pixel. Bits can be 24, 16, 15, 14, 32 (treated as 24), or 0: 95 24: RRRRRRRR GGGGGGGG BBBBBBBB (8-8-8 RGB) 96 16: RRRRRGGG GGGBBBBB (5-6-5 RGB) 97 15: RRRRRGG GGGBBBBB (5-5-5 RGB) 98 14: BBBBBGG GGGRRRRR (5-5-5 BGR, native SNES format) 99 0: xxxRRRRR RRRxxGGG GGGGGxxB BBBBBBBx (native internal format; x = junk bits) */ 100 #define SNES_NTSC_RGB_OUT( index, rgb_out, bits ) \ 101 SNES_NTSC_RGB_OUT_14_( index, rgb_out, bits, 1 ) 102 103 /* Hires equivalents */ 104 #define SNES_NTSC_HIRES_ROW( ntsc, burst, pixel1, pixel2, pixel3, pixel4, pixel5 ) \ 105 char const* ktable = \ 106 (char const*) (ntsc)->table + burst * (snes_ntsc_burst_size * sizeof (snes_ntsc_rgb_t));\ 107 unsigned const snes_ntsc_pixel1_ = (pixel1);\ 108 snes_ntsc_rgb_t const* kernel1 = SNES_NTSC_IN_FORMAT( ktable, snes_ntsc_pixel1_ );\ 109 unsigned const snes_ntsc_pixel2_ = (pixel2);\ 110 snes_ntsc_rgb_t const* kernel2 = SNES_NTSC_IN_FORMAT( ktable, snes_ntsc_pixel2_ );\ 111 unsigned const snes_ntsc_pixel3_ = (pixel3);\ 112 snes_ntsc_rgb_t const* kernel3 = SNES_NTSC_IN_FORMAT( ktable, snes_ntsc_pixel3_ );\ 113 unsigned const snes_ntsc_pixel4_ = (pixel4);\ 114 snes_ntsc_rgb_t const* kernel4 = SNES_NTSC_IN_FORMAT( ktable, snes_ntsc_pixel4_ );\ 115 unsigned const snes_ntsc_pixel5_ = (pixel5);\ 116 snes_ntsc_rgb_t const* kernel5 = SNES_NTSC_IN_FORMAT( ktable, snes_ntsc_pixel5_ );\ 117 snes_ntsc_rgb_t const* kernel0 = kernel1;\ 118 snes_ntsc_rgb_t const* kernelx0;\ 119 snes_ntsc_rgb_t const* kernelx1 = kernel1;\ 120 snes_ntsc_rgb_t const* kernelx2 = kernel1;\ 121 snes_ntsc_rgb_t const* kernelx3 = kernel1;\ 122 snes_ntsc_rgb_t const* kernelx4 = kernel1;\ 123 snes_ntsc_rgb_t const* kernelx5 = kernel1 124 125 #define SNES_NTSC_HIRES_OUT( x, rgb_out, bits ) {\ 126 snes_ntsc_rgb_t raw_ =\ 127 kernel0 [ x ] + kernel2 [(x+5)%7+14] + kernel4 [(x+3)%7+28] +\ 128 kernelx0 [(x+7)%7+7] + kernelx2 [(x+5)%7+21] + kernelx4 [(x+3)%7+35] +\ 129 kernel1 [(x+6)%7 ] + kernel3 [(x+4)%7+14] + kernel5 [(x+2)%7+28] +\ 130 kernelx1 [(x+6)%7+7] + kernelx3 [(x+4)%7+21] + kernelx5 [(x+2)%7+35];\ 131 SNES_NTSC_CLAMP_( raw_, 0 );\ 132 SNES_NTSC_RGB_OUT_( rgb_out, (bits), 0 );\ 133 } 134 135 136 /* private */ 137 enum { snes_ntsc_entry_size = 128 }; 138 enum { snes_ntsc_palette_size = 0x2000 }; 139 typedef unsigned long snes_ntsc_rgb_t; 140 struct snes_ntsc_t { 141 snes_ntsc_rgb_t table [snes_ntsc_palette_size] [snes_ntsc_entry_size]; 142 }; 143 enum { snes_ntsc_burst_size = snes_ntsc_entry_size / snes_ntsc_burst_count }; 144 145 #define SNES_NTSC_RGB16( ktable, n ) \ 146 (snes_ntsc_rgb_t const*) (ktable + ((n & 0x001E) | (n >> 1 & 0x03E0) | (n >> 2 & 0x3C00)) * \ 147 (snes_ntsc_entry_size / 2 * sizeof (snes_ntsc_rgb_t))) 148 149 #define SNES_NTSC_BGR15( ktable, n ) \ 150 (snes_ntsc_rgb_t const*) (ktable + ((n << 9 & 0x3C00) | (n & 0x03E0) | (n >> 10 & 0x001E)) * \ 151 (snes_ntsc_entry_size / 2 * sizeof (snes_ntsc_rgb_t))) 152 153 /* common 3->7 ntsc macros */ 154 #define SNES_NTSC_BEGIN_ROW_6_( pixel0, pixel1, pixel2, ENTRY, table ) \ 155 unsigned const snes_ntsc_pixel0_ = (pixel0);\ 156 snes_ntsc_rgb_t const* kernel0 = ENTRY( table, snes_ntsc_pixel0_ );\ 157 unsigned const snes_ntsc_pixel1_ = (pixel1);\ 158 snes_ntsc_rgb_t const* kernel1 = ENTRY( table, snes_ntsc_pixel1_ );\ 159 unsigned const snes_ntsc_pixel2_ = (pixel2);\ 160 snes_ntsc_rgb_t const* kernel2 = ENTRY( table, snes_ntsc_pixel2_ );\ 161 snes_ntsc_rgb_t const* kernelx0;\ 162 snes_ntsc_rgb_t const* kernelx1 = kernel0;\ 163 snes_ntsc_rgb_t const* kernelx2 = kernel0 164 165 #define SNES_NTSC_RGB_OUT_14_( x, rgb_out, bits, shift ) {\ 166 snes_ntsc_rgb_t raw_ =\ 167 kernel0 [x ] + kernel1 [(x+12)%7+14] + kernel2 [(x+10)%7+28] +\ 168 kernelx0 [(x+7)%14] + kernelx1 [(x+ 5)%7+21] + kernelx2 [(x+ 3)%7+35];\ 169 SNES_NTSC_CLAMP_( raw_, shift );\ 170 SNES_NTSC_RGB_OUT_( rgb_out, bits, shift );\ 171 } 172 173 /* common ntsc macros */ 174 #define snes_ntsc_rgb_builder ((1L << 21) | (1 << 11) | (1 << 1)) 175 #define snes_ntsc_clamp_mask (snes_ntsc_rgb_builder * 3 / 2) 176 #define snes_ntsc_clamp_add (snes_ntsc_rgb_builder * 0x101) 177 #define SNES_NTSC_CLAMP_( io, shift ) {\ 178 snes_ntsc_rgb_t sub = (io) >> (9-(shift)) & snes_ntsc_clamp_mask;\ 179 snes_ntsc_rgb_t clamp = snes_ntsc_clamp_add - sub;\ 180 io |= clamp;\ 181 clamp -= sub;\ 182 io &= clamp;\ 183 } 184 185 #define SNES_NTSC_COLOR_IN_( index, color, ENTRY, table ) {\ 186 unsigned color_;\ 187 kernelx##index = kernel##index;\ 188 kernel##index = (color_ = (color), ENTRY( table, color_ ));\ 189 } 190 191 /* x is always zero except in snes_ntsc library */ 192 #define SNES_NTSC_RGB_OUT_( rgb_out, bits, x ) {\ 193 if ( bits == 16 )\ 194 rgb_out = (raw_>>(13-x)& 0xF800)|(raw_>>(8-x)&0x07E0)|(raw_>>(4-x)&0x001F);\ 195 if ( bits == 32 )\ 196 rgb_out = (raw_>>(5-x)&0xFF0000)|(raw_>>(3-x)&0xFF00)|(raw_>>(1-x)&0xFF) | (0xff << 24);\ 197 if ( bits == 24 )\ 198 rgb_out = (raw_>>(5-x)&0xFF0000)|(raw_>>(3-x)&0xFF00)|(raw_>>(1-x)&0xFF);\ 199 if ( bits == 15 )\ 200 rgb_out = (raw_>>(14-x)& 0x7C00)|(raw_>>(9-x)&0x03E0)|(raw_>>(4-x)&0x001F);\ 201 if ( bits == 14 )\ 202 rgb_out = (raw_>>(24-x)& 0x001F)|(raw_>>(9-x)&0x03E0)|(raw_<<(6+x)&0x7C00);\ 203 if ( bits == 0 )\ 204 rgb_out = raw_ << x;\ 205 } 206 207 #ifdef __cplusplus 208 } 209 #endif 210 211 #endif 212