1 /* nes_ntsc 0.2.2. http://www.slack.net/~ant/ */
2 
3 #include "nes_ntsc.h"
4 
5 /* Copyright (C) 2006-2007 Shay Green. This module is free software; you
6 can redistribute it and/or modify it under the terms of the GNU Lesser
7 General Public License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version. This
9 module is distributed in the hope that it will be useful, but WITHOUT ANY
10 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
12 details. You should have received a copy of the GNU Lesser General Public
13 License along with this module; if not, write to the Free Software Foundation,
14 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
15 
16 nes_ntsc_setup_t const nes_ntsc_monochrome = { 0,-1, 0, 0,.2,  0,.2,-.2,-.2,-1, 1, 0, 0, 0, 0 };
17 nes_ntsc_setup_t const nes_ntsc_composite  = { 0, 0, 0, 0, 0,  0, 0,  0,  0, 0, 1, 0, 0, 0, 0 };
18 nes_ntsc_setup_t const nes_ntsc_svideo     = { 0, 0, 0, 0,.2,  0,.2, -1, -1, 0, 1, 0, 0, 0, 0 };
19 nes_ntsc_setup_t const nes_ntsc_rgb        = { 0, 0, 0, 0,.2,  0,.7, -1, -1,-1, 1, 0, 0, 0, 0 };
20 
21 #define alignment_count 3
22 #define burst_count     3
23 #define rescale_in      8
24 #define rescale_out     7
25 
26 #define artifacts_mid   1.0f
27 #define fringing_mid    1.0f
28 #define std_decoder_hue -15
29 
30 #define STD_HUE_CONDITION( setup ) !(setup->base_palette || setup->palette)
31 
32 #include "nes_ntsc_impl.h"
33 
34 /* 3 input pixels -> 8 composite samples */
35 pixel_info_t const nes_ntsc_pixels [alignment_count] = {
36 	{ PIXEL_OFFSET( -4, -9 ), { 1, 1, .6667f, 0 } },
37 	{ PIXEL_OFFSET( -2, -7 ), {       .3333f, 1, 1, .3333f } },
38 	{ PIXEL_OFFSET(  0, -5 ), {                  0, .6667f, 1, 1 } },
39 };
40 
merge_kernel_fields(nes_ntsc_rgb_t * io)41 static void merge_kernel_fields( nes_ntsc_rgb_t* io )
42 {
43 	int n;
44 	for ( n = burst_size; n; --n )
45 	{
46 		nes_ntsc_rgb_t p0 = io [burst_size * 0] + rgb_bias;
47 		nes_ntsc_rgb_t p1 = io [burst_size * 1] + rgb_bias;
48 		nes_ntsc_rgb_t p2 = io [burst_size * 2] + rgb_bias;
49 		/* merge colors without losing precision */
50 		io [burst_size * 0] =
51 				((p0 + p1 - ((p0 ^ p1) & nes_ntsc_rgb_builder)) >> 1) - rgb_bias;
52 		io [burst_size * 1] =
53 				((p1 + p2 - ((p1 ^ p2) & nes_ntsc_rgb_builder)) >> 1) - rgb_bias;
54 		io [burst_size * 2] =
55 				((p2 + p0 - ((p2 ^ p0) & nes_ntsc_rgb_builder)) >> 1) - rgb_bias;
56 		++io;
57 	}
58 }
59 
correct_errors(nes_ntsc_rgb_t color,nes_ntsc_rgb_t * out)60 static void correct_errors( nes_ntsc_rgb_t color, nes_ntsc_rgb_t* out )
61 {
62 	int n;
63 	for ( n = burst_count; n; --n )
64 	{
65 		unsigned i;
66 		for ( i = 0; i < rgb_kernel_size / 2; i++ )
67 		{
68 			nes_ntsc_rgb_t error = color -
69 					out [i    ] - out [(i+12)%14+14] - out [(i+10)%14+28] -
70 					out [i + 7] - out [i + 5    +14] - out [i + 3    +28];
71 			DISTRIBUTE_ERROR( i+3+28, i+5+14, i+7 );
72 		}
73 		out += alignment_count * rgb_kernel_size;
74 	}
75 }
76 
nes_ntsc_init(nes_ntsc_t * ntsc,nes_ntsc_setup_t const * setup)77 void nes_ntsc_init( nes_ntsc_t* ntsc, nes_ntsc_setup_t const* setup )
78 {
79 	int merge_fields;
80 	int entry;
81 	init_t impl;
82 	float gamma_factor;
83 
84 	if ( !setup )
85 		setup = &nes_ntsc_composite;
86 	init( &impl, setup );
87 
88 	/* setup fast gamma */
89 	{
90 		float gamma = (float) setup->gamma * -0.5f;
91 		if ( STD_HUE_CONDITION( setup ) )
92 			gamma += 0.1333f;
93 
94 		gamma_factor = (float) pow( (float) fabs( gamma ), 0.73f );
95 		if ( gamma < 0 )
96 			gamma_factor = -gamma_factor;
97 	}
98 
99 	merge_fields = setup->merge_fields;
100 	if ( setup->artifacts <= -1 && setup->fringing <= -1 )
101 		merge_fields = 1;
102 
103 	for ( entry = 0; entry < nes_ntsc_palette_size; entry++ )
104 	{
105 		/* Base 64-color generation */
106 		static float const lo_levels [4] = { -0.12f, 0.00f, 0.31f, 0.72f };
107 		static float const hi_levels [4] = {  0.40f, 0.68f, 1.00f, 1.00f };
108 		int level = entry >> 4 & 0x03;
109 		float lo = lo_levels [level];
110 		float hi = hi_levels [level];
111 
112 		int color = entry & 0x0F;
113 		if ( color == 0 )
114 			lo = hi;
115 		if ( color == 0x0D )
116 			hi = lo;
117 		if ( color > 0x0D )
118 			hi = lo = 0.0f;
119 
120 		{
121 			/* phases [i] = cos( i * PI / 6 ) */
122 			static float const phases [0x10 + 3] = {
123 				-1.0f, -0.866025f, -0.5f, 0.0f,  0.5f,  0.866025f,
124 				 1.0f,  0.866025f,  0.5f, 0.0f, -0.5f, -0.866025f,
125 				-1.0f, -0.866025f, -0.5f, 0.0f,  0.5f,  0.866025f,
126 				 1.0f
127 			};
128 			#define TO_ANGLE_SIN( color )   phases [color]
129 			#define TO_ANGLE_COS( color )   phases [(color) + 3]
130 
131 			/* Convert raw waveform to YIQ */
132 			float sat = (hi - lo) * 0.5f;
133 			float i = TO_ANGLE_SIN( color ) * sat;
134 			float q = TO_ANGLE_COS( color ) * sat;
135 			float y = (hi + lo) * 0.5f;
136 
137 			/* Optionally use base palette instead */
138 			if ( setup->base_palette )
139 			{
140 				unsigned char const* in = &setup->base_palette [(entry & 0x3F) * 3];
141 				static float const to_float = 1.0f / 0xFF;
142 				float r = to_float * in [0];
143 				float g = to_float * in [1];
144 				float b = to_float * in [2];
145 				q = RGB_TO_YIQ( r, g, b, y, i );
146 			}
147 
148 			/* Apply color emphasis */
149 			#ifdef NES_NTSC_EMPHASIS
150 			{
151 				int tint = entry >> 6 & 7;
152 				if ( tint && color <= 0x0D )
153 				{
154 					static float const atten_mul = 0.79399f;
155 					static float const atten_sub = 0.0782838f;
156 
157 					if ( tint == 7 )
158 					{
159 						y = y * (atten_mul * 1.13f) - (atten_sub * 1.13f);
160 					}
161 					else
162 					{
163 						static unsigned char const tints [8] = { 0, 6, 10, 8, 2, 4, 0, 0 };
164 						int const tint_color = tints [tint];
165 						float sat = hi * (0.5f - atten_mul * 0.5f) + atten_sub * 0.5f;
166 						y -= sat * 0.5f;
167 						if ( tint >= 3 && tint != 4 )
168 						{
169 							/* combined tint bits */
170 							sat *= 0.6f;
171 							y -= sat;
172 						}
173 						i += TO_ANGLE_SIN( tint_color ) * sat;
174 						q += TO_ANGLE_COS( tint_color ) * sat;
175 					}
176 				}
177 			}
178 			#endif
179 
180 			/* Optionally use palette instead */
181 			if ( setup->palette )
182 			{
183 				unsigned char const* in = &setup->palette [entry * 3];
184 				static float const to_float = 1.0f / 0xFF;
185 				float r = to_float * in [0];
186 				float g = to_float * in [1];
187 				float b = to_float * in [2];
188 				q = RGB_TO_YIQ( r, g, b, y, i );
189 			}
190 
191 			/* Apply brightness, contrast, and gamma */
192 			y *= (float) setup->contrast * 0.5f + 1;
193 			/* adjustment reduces error when using input palette */
194 			y += (float) setup->brightness * 0.5f - 0.5f / 256;
195 
196 			{
197 				float r, g, b = YIQ_TO_RGB( y, i, q, default_decoder, float, r, g );
198 
199 				/* fast approximation of n = pow( n, gamma ) */
200 				r = (r * gamma_factor - gamma_factor) * r + r;
201 				g = (g * gamma_factor - gamma_factor) * g + g;
202 				b = (b * gamma_factor - gamma_factor) * b + b;
203 
204 				q = RGB_TO_YIQ( r, g, b, y, i );
205 			}
206 
207 			i *= rgb_unit;
208 			q *= rgb_unit;
209 			y *= rgb_unit;
210 			y += rgb_offset;
211 
212 			/* Generate kernel */
213 			{
214 				int r, g, b = YIQ_TO_RGB( y, i, q, impl.to_rgb, int, r, g );
215 				/* blue tends to overflow, so clamp it */
216 				nes_ntsc_rgb_t rgb = PACK_RGB( r, g, (b < 0x3E0 ? b: 0x3E0) );
217 
218 				if ( setup->palette_out )
219 					RGB_PALETTE_OUT( rgb, &setup->palette_out [entry * 3] );
220 
221 				if ( ntsc )
222 				{
223 					nes_ntsc_rgb_t* kernel = ntsc->table [entry];
224 					gen_kernel( &impl, y, i, q, kernel );
225 					if ( merge_fields )
226 						merge_kernel_fields( kernel );
227 					correct_errors( rgb, kernel );
228 				}
229 			}
230 		}
231 	}
232 }
233 
234 #ifndef NES_NTSC_NO_BLITTERS
235 
nes_ntsc_blit(nes_ntsc_t const * ntsc,NES_NTSC_IN_T const * input,NES_NTSC_IN_T const * inputD,long in_row_width,int burst_phase,int in_width,int in_height,void * rgb_out,long out_pitch)236 void nes_ntsc_blit( nes_ntsc_t const* ntsc, NES_NTSC_IN_T const* input, NES_NTSC_IN_T const* inputD, long in_row_width,
237 		int burst_phase, int in_width, int in_height, void* rgb_out, long out_pitch )
238 {
239 	int chunk_count = (in_width - 1) / nes_ntsc_in_chunk;
240 	for ( ; in_height; --in_height )
241 	{
242 		NES_NTSC_IN_T const* line_in = input;
243 		NES_NTSC_IN_T const* line_inD = inputD;
244 		NES_NTSC_BEGIN_ROW( ntsc, burst_phase,
245 				nes_ntsc_black, nes_ntsc_black, NES_NTSC_ADJ_IN( *line_in, *line_inD ) );
246 		nes_ntsc_out_t* line_out = (nes_ntsc_out_t*) rgb_out;
247 		int n;
248 		++line_in;
249 
250 		for ( n = chunk_count; n; --n )
251 		{
252 			/* order of input and output pixels must not be altered */
253 			NES_NTSC_COLOR_IN( 0, NES_NTSC_ADJ_IN( line_in [0], line_inD[0] ) );
254 			NES_NTSC_RGB_OUT( 0, line_out [0], NES_NTSC_OUT_DEPTH );
255 			NES_NTSC_RGB_OUT( 1, line_out [1], NES_NTSC_OUT_DEPTH );
256 
257 			NES_NTSC_COLOR_IN( 1, NES_NTSC_ADJ_IN( line_in [1], line_inD[1] ) );
258 			NES_NTSC_RGB_OUT( 2, line_out [2], NES_NTSC_OUT_DEPTH );
259 			NES_NTSC_RGB_OUT( 3, line_out [3], NES_NTSC_OUT_DEPTH );
260 
261 			NES_NTSC_COLOR_IN( 2, NES_NTSC_ADJ_IN( line_in [2], line_inD[2] ) );
262 			NES_NTSC_RGB_OUT( 4, line_out [4], NES_NTSC_OUT_DEPTH );
263 			NES_NTSC_RGB_OUT( 5, line_out [5], NES_NTSC_OUT_DEPTH );
264 			NES_NTSC_RGB_OUT( 6, line_out [6], NES_NTSC_OUT_DEPTH );
265 
266 			line_in  += 3;
267 			line_inD += 3;
268 			line_out += 7;
269 		}
270 
271 		/* finish final pixels */
272 		NES_NTSC_COLOR_IN( 0, nes_ntsc_black );
273 		NES_NTSC_RGB_OUT( 0, line_out [0], NES_NTSC_OUT_DEPTH );
274 		NES_NTSC_RGB_OUT( 1, line_out [1], NES_NTSC_OUT_DEPTH );
275 
276 		NES_NTSC_COLOR_IN( 1, nes_ntsc_black );
277 		NES_NTSC_RGB_OUT( 2, line_out [2], NES_NTSC_OUT_DEPTH );
278 		NES_NTSC_RGB_OUT( 3, line_out [3], NES_NTSC_OUT_DEPTH );
279 
280 		NES_NTSC_COLOR_IN( 2, nes_ntsc_black );
281 		NES_NTSC_RGB_OUT( 4, line_out [4], NES_NTSC_OUT_DEPTH );
282 		NES_NTSC_RGB_OUT( 5, line_out [5], NES_NTSC_OUT_DEPTH );
283 		NES_NTSC_RGB_OUT( 6, line_out [6], NES_NTSC_OUT_DEPTH );
284 
285 		burst_phase = (burst_phase + 1) % nes_ntsc_burst_count;
286 		input  += in_row_width;
287 		inputD += in_row_width;
288 		rgb_out = (char*) rgb_out + out_pitch;
289 	}
290 }
291 
292 #endif
293