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