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
14in parenthesis and should remain fairly stable in future versions. */
15typedef 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 */
37extern snes_ntsc_setup_t const snes_ntsc_composite; /* color bleeding + artifacts */
38extern snes_ntsc_setup_t const snes_ntsc_svideo; /* color bleeding only */
39extern snes_ntsc_setup_t const snes_ntsc_rgb; /* crisp image */
40extern snes_ntsc_setup_t const snes_ntsc_monochrome;/* desaturated + artifacts */
41
42/* Scanline values */
43extern unsigned int snes_ntsc_scanline_offset;
44extern unsigned short snes_ntsc_scanline_mask;
45
46/* Initializes and adjusts parameters. Can be called multiple times on the same
47snes_ntsc_t object. Can pass NULL for either parameter. */
48typedef struct snes_ntsc_t snes_ntsc_t;
49void 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
52and output RGB depth is set by SNES_NTSC_OUT_DEPTH. Both default to 16-bit RGB.
53In_row_width is the number of pixels to get to the next input row. Out_pitch
54is the number of *bytes* to get to the next output row. */
55void 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
59void 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
63void 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
67void 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
72might be rounded down slightly; use SNES_NTSC_IN_WIDTH() on result to find rounded
73value. 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
78rounded down slightly; use SNES_NTSC_OUT_WIDTH() on result to find rounded
79value. */
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
86enum { snes_ntsc_in_chunk = 3 }; /* number of input pixels read per chunk */
87enum { snes_ntsc_out_chunk = 7 }; /* number of output pixels generated per chunk */
88enum { snes_ntsc_black = 0 }; /* palette index for black */
89enum { 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.
92Use snes_ntsc_black for unused pixels. Declares variables, so must be before first
93statement 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:
10424: RRRRRRRR GGGGGGGG BBBBBBBB (8-8-8 RGB)
10516: RRRRRGGG GGGBBBBB (5-6-5 RGB)
10615: RRRRRGG GGGBBBBB (5-5-5 RGB)
10714: 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 */
146enum { snes_ntsc_entry_size = 128 };
147enum { snes_ntsc_palette_size = 0x2000 };
148typedef unsigned long snes_ntsc_rgb_t;
149struct snes_ntsc_t {
150 snes_ntsc_rgb_t table [snes_ntsc_palette_size] [snes_ntsc_entry_size];
151};
152enum { 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