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