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