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 | /* Scanline values */ |
43 | extern unsigned int snes_ntsc_scanline_offset; |
44 | extern unsigned short snes_ntsc_scanline_mask; |
45 | |
46 | /* Initializes and adjusts parameters. Can be called multiple times on the same |
47 | snes_ntsc_t object. Can pass NULL for either parameter. */ |
48 | typedef struct snes_ntsc_t snes_ntsc_t; |
49 | void snes_ntsc_init( snes_ntsc_t* ntsc, snes_ntsc_setup_t const* setup ); |
50 | |
51 | /* Filters one or more rows of pixels. Input pixel format is set by SNES_NTSC_IN_FORMAT |
52 | and output RGB depth is set by SNES_NTSC_OUT_DEPTH. Both default to 16-bit RGB. |
53 | In_row_width is the number of pixels to get to the next input row. Out_pitch |
54 | is the number of *bytes* to get to the next output row. */ |
55 | void snes_ntsc_blit( 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 | void snes_ntsc_blit_hires( snes_ntsc_t const* ntsc, SNES_NTSC_IN_T const* input, |
60 | long in_row_width, int burst_phase, int in_width, int in_height, |
61 | void* rgb_out, long out_pitch ); |
62 | |
63 | void snes_ntsc_blit_scanlines( snes_ntsc_t const* ntsc, SNES_NTSC_IN_T const* input, |
64 | long in_row_width, int burst_phase, int in_width, int in_height, |
65 | void* rgb_out, long out_pitch ); |
66 | |
67 | void snes_ntsc_blit_hires_scanlines( snes_ntsc_t const* ntsc, SNES_NTSC_IN_T const* input, |
68 | long in_row_width, int burst_phase, int in_width, int in_height, |
69 | void* rgb_out, long out_pitch ); |
70 | |
71 | /* Number of output pixels written by low-res blitter for given input width. Width |
72 | might be rounded down slightly; use SNES_NTSC_IN_WIDTH() on result to find rounded |
73 | value. Guaranteed not to round 256 down at all. */ |
74 | #define SNES_NTSC_OUT_WIDTH( in_width ) \ |
75 | ((((in_width) - 1) / snes_ntsc_in_chunk + 1) * snes_ntsc_out_chunk) |
76 | |
77 | /* Number of low-res input pixels that will fit within given output width. Might be |
78 | rounded down slightly; use SNES_NTSC_OUT_WIDTH() on result to find rounded |
79 | value. */ |
80 | #define SNES_NTSC_IN_WIDTH( out_width ) \ |
81 | (((out_width) / snes_ntsc_out_chunk - 1) * snes_ntsc_in_chunk + 1) |
82 | |
83 | |
84 | /* Interface for user-defined custom blitters */ |
85 | |
86 | enum { snes_ntsc_in_chunk = 3 }; /* number of input pixels read per chunk */ |
87 | enum { snes_ntsc_out_chunk = 7 }; /* number of output pixels generated per chunk */ |
88 | enum { snes_ntsc_black = 0 }; /* palette index for black */ |
89 | enum { snes_ntsc_burst_count = 3 }; /* burst phase cycles through 0, 1, and 2 */ |
90 | |
91 | /* Begins outputting row and starts three pixels. First pixel will be cut off a bit. |
92 | Use snes_ntsc_black for unused pixels. Declares variables, so must be before first |
93 | statement in a block (unless you're using C++). */ |
94 | #define SNES_NTSC_BEGIN_ROW( ntsc, burst, pixel0, pixel1, pixel2 ) \ |
95 | char const* ktable = \ |
96 | (char const*) (ntsc)->table + burst * (snes_ntsc_burst_size * sizeof (snes_ntsc_rgb_t));\ |
97 | SNES_NTSC_BEGIN_ROW_6_( pixel0, pixel1, pixel2, SNES_NTSC_IN_FORMAT, ktable ) |
98 | |
99 | /* Begins input pixel */ |
100 | #define SNES_NTSC_COLOR_IN( index, color ) \ |
101 | SNES_NTSC_COLOR_IN_( index, color, SNES_NTSC_IN_FORMAT, ktable ) |
102 | |
103 | /* Generates output pixel. Bits can be 24, 16, 15, 14, 32 (treated as 24), or 0: |
104 | 24: RRRRRRRR GGGGGGGG BBBBBBBB (8-8-8 RGB) |
105 | 16: RRRRRGGG GGGBBBBB (5-6-5 RGB) |
106 | 15: RRRRRGG GGGBBBBB (5-5-5 RGB) |
107 | 14: BBBBBGG GGGRRRRR (5-5-5 BGR, native SNES format) |
108 | 0: xxxRRRRR RRRxxGGG GGGGGxxB BBBBBBBx (native internal format; x = junk bits) */ |
109 | #define SNES_NTSC_RGB_OUT( index, rgb_out, bits ) \ |
110 | SNES_NTSC_RGB_OUT_14_( index, rgb_out, bits, 1 ) |
111 | |
112 | /* Hires equivalents */ |
113 | #define SNES_NTSC_HIRES_ROW( ntsc, burst, pixel1, pixel2, pixel3, pixel4, pixel5 ) \ |
114 | char const* ktable = \ |
115 | (char const*) (ntsc)->table + burst * (snes_ntsc_burst_size * sizeof (snes_ntsc_rgb_t));\ |
116 | unsigned const snes_ntsc_pixel1_ = (pixel1);\ |
117 | snes_ntsc_rgb_t const* kernel1 = SNES_NTSC_IN_FORMAT( ktable, snes_ntsc_pixel1_ );\ |
118 | unsigned const snes_ntsc_pixel2_ = (pixel2);\ |
119 | snes_ntsc_rgb_t const* kernel2 = SNES_NTSC_IN_FORMAT( ktable, snes_ntsc_pixel2_ );\ |
120 | unsigned const snes_ntsc_pixel3_ = (pixel3);\ |
121 | snes_ntsc_rgb_t const* kernel3 = SNES_NTSC_IN_FORMAT( ktable, snes_ntsc_pixel3_ );\ |
122 | unsigned const snes_ntsc_pixel4_ = (pixel4);\ |
123 | snes_ntsc_rgb_t const* kernel4 = SNES_NTSC_IN_FORMAT( ktable, snes_ntsc_pixel4_ );\ |
124 | unsigned const snes_ntsc_pixel5_ = (pixel5);\ |
125 | snes_ntsc_rgb_t const* kernel5 = SNES_NTSC_IN_FORMAT( ktable, snes_ntsc_pixel5_ );\ |
126 | snes_ntsc_rgb_t const* kernel0 = kernel1;\ |
127 | snes_ntsc_rgb_t const* kernelx0;\ |
128 | snes_ntsc_rgb_t const* kernelx1 = kernel1;\ |
129 | snes_ntsc_rgb_t const* kernelx2 = kernel1;\ |
130 | snes_ntsc_rgb_t const* kernelx3 = kernel1;\ |
131 | snes_ntsc_rgb_t const* kernelx4 = kernel1;\ |
132 | snes_ntsc_rgb_t const* kernelx5 = kernel1 |
133 | |
134 | #define SNES_NTSC_HIRES_OUT( x, rgb_out, bits ) {\ |
135 | snes_ntsc_rgb_t raw_ =\ |
136 | kernel0 [ x ] + kernel2 [(x+5)%7+14] + kernel4 [(x+3)%7+28] +\ |
137 | kernelx0 [(x+7)%7+7] + kernelx2 [(x+5)%7+21] + kernelx4 [(x+3)%7+35] +\ |
138 | kernel1 [(x+6)%7 ] + kernel3 [(x+4)%7+14] + kernel5 [(x+2)%7+28] +\ |
139 | kernelx1 [(x+6)%7+7] + kernelx3 [(x+4)%7+21] + kernelx5 [(x+2)%7+35];\ |
140 | SNES_NTSC_CLAMP_( raw_, 0 );\ |
141 | SNES_NTSC_RGB_OUT_( rgb_out, (bits), 0 );\ |
142 | } |
143 | |
144 | |
145 | /* private */ |
146 | enum { snes_ntsc_entry_size = 128 }; |
147 | enum { snes_ntsc_palette_size = 0x2000 }; |
148 | typedef unsigned long snes_ntsc_rgb_t; |
149 | struct snes_ntsc_t { |
150 | snes_ntsc_rgb_t table [snes_ntsc_palette_size] [snes_ntsc_entry_size]; |
151 | }; |
152 | enum { snes_ntsc_burst_size = snes_ntsc_entry_size / snes_ntsc_burst_count }; |
153 | |
154 | #define SNES_NTSC_RGB16( ktable, n ) \ |
155 | (snes_ntsc_rgb_t const*) (ktable + ((n & 0x001E) | (n >> 1 & 0x03E0) | (n >> 2 & 0x3C00)) * \ |
156 | (snes_ntsc_entry_size / 2 * sizeof (snes_ntsc_rgb_t))) |
157 | |
158 | #define SNES_NTSC_RGB15( ktable, n ) \ |
159 | (snes_ntsc_rgb_t const*) (ktable + ((n & 0x001E) | (n >> 0 & 0x03E0) | (n >> 1 & 0x3C00)) * \ |
160 | (snes_ntsc_entry_size / 2 * sizeof (snes_ntsc_rgb_t))) |
161 | |
162 | #define SNES_NTSC_BGR15( ktable, n ) \ |
163 | (snes_ntsc_rgb_t const*) (ktable + ((n << 9 & 0x3C00) | (n & 0x03E0) | (n >> 10 & 0x001E)) * \ |
164 | (snes_ntsc_entry_size / 2 * sizeof (snes_ntsc_rgb_t))) |
165 | |
166 | /* common 3->7 ntsc macros */ |
167 | #define SNES_NTSC_BEGIN_ROW_6_( pixel0, pixel1, pixel2, ENTRY, table ) \ |
168 | unsigned const snes_ntsc_pixel0_ = (pixel0);\ |
169 | snes_ntsc_rgb_t const* kernel0 = ENTRY( table, snes_ntsc_pixel0_ );\ |
170 | unsigned const snes_ntsc_pixel1_ = (pixel1);\ |
171 | snes_ntsc_rgb_t const* kernel1 = ENTRY( table, snes_ntsc_pixel1_ );\ |
172 | unsigned const snes_ntsc_pixel2_ = (pixel2);\ |
173 | snes_ntsc_rgb_t const* kernel2 = ENTRY( table, snes_ntsc_pixel2_ );\ |
174 | snes_ntsc_rgb_t const* kernelx0;\ |
175 | snes_ntsc_rgb_t const* kernelx1 = kernel0;\ |
176 | snes_ntsc_rgb_t const* kernelx2 = kernel0 |
177 | |
178 | #define SNES_NTSC_RGB_OUT_14_( x, rgb_out, bits, shift ) {\ |
179 | snes_ntsc_rgb_t raw_ =\ |
180 | kernel0 [x ] + kernel1 [(x+12)%7+14] + kernel2 [(x+10)%7+28] +\ |
181 | kernelx0 [(x+7)%14] + kernelx1 [(x+ 5)%7+21] + kernelx2 [(x+ 3)%7+35];\ |
182 | SNES_NTSC_CLAMP_( raw_, shift );\ |
183 | SNES_NTSC_RGB_OUT_( rgb_out, bits, shift );\ |
184 | } |
185 | |
186 | /* common ntsc macros */ |
187 | #define snes_ntsc_rgb_builder ((1L << 21) | (1 << 11) | (1 << 1)) |
188 | #define snes_ntsc_clamp_mask (snes_ntsc_rgb_builder * 3 / 2) |
189 | #define snes_ntsc_clamp_add (snes_ntsc_rgb_builder * 0x101) |
190 | #define SNES_NTSC_CLAMP_( io, shift ) {\ |
191 | snes_ntsc_rgb_t sub = (io) >> (9-(shift)) & snes_ntsc_clamp_mask;\ |
192 | snes_ntsc_rgb_t clamp = snes_ntsc_clamp_add - sub;\ |
193 | io |= clamp;\ |
194 | clamp -= sub;\ |
195 | io &= clamp;\ |
196 | } |
197 | |
198 | #define SNES_NTSC_COLOR_IN_( index, color, ENTRY, table ) {\ |
199 | unsigned color_;\ |
200 | kernelx##index = kernel##index;\ |
201 | kernel##index = (color_ = (color), ENTRY( table, color_ ));\ |
202 | } |
203 | |
204 | /* x is always zero except in snes_ntsc library */ |
205 | #define SNES_NTSC_RGB_OUT_( rgb_out, bits, x ) {\ |
206 | if ( bits == 16 )\ |
207 | rgb_out = (raw_>>(13-x)& 0xF800)|(raw_>>(8-x)&0x07E0)|(raw_>>(4-x)&0x001F);\ |
208 | if ( bits == 24 || bits == 32 )\ |
209 | rgb_out = (raw_>>(5-x)&0xFF0000)|(raw_>>(3-x)&0xFF00)|(raw_>>(1-x)&0xFF);\ |
210 | if ( bits == 15 )\ |
211 | rgb_out = (raw_>>(14-x)& 0x7C00)|(raw_>>(9-x)&0x03E0)|(raw_>>(4-x)&0x001F);\ |
212 | if ( bits == 14 )\ |
213 | rgb_out = (raw_>>(24-x)& 0x001F)|(raw_>>(9-x)&0x03E0)|(raw_<<(6+x)&0x7C00);\ |
214 | if ( bits == 0 )\ |
215 | rgb_out = raw_ << x;\ |
216 | } |
217 | |
218 | #ifdef __cplusplus |
219 | } |
220 | #endif |
221 | |
222 | #endif |
223 | |