1 | /* snes_ntsc 0.2.2. http://www.slack.net/~ant/ */ |
2 | |
3 | #include "snes_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 | snes_ntsc_setup_t const snes_ntsc_monochrome = { 0,-1, 0, 0,.2, 0,.2,-.2,-.2,-1, 1, 0, 0 }; |
17 | snes_ntsc_setup_t const snes_ntsc_composite = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }; |
18 | snes_ntsc_setup_t const snes_ntsc_svideo = { 0, 0, 0, 0,.2, 0,.2, -1, -1, 0, 1, 0, 0 }; |
19 | snes_ntsc_setup_t const snes_ntsc_rgb = { 0, 0, 0, 0,.2, 0,.7, -1, -1,-1, 1, 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 0 |
29 | |
30 | #define rgb_bits 7 /* half normal range to allow for doubled hires pixels */ |
31 | #define gamma_size 32 |
32 | |
33 | #include "snes_ntsc_impl.h" |
34 | |
35 | unsigned int snes_ntsc_scanline_offset = 0; |
36 | unsigned short snes_ntsc_scanline_mask = 0xffff; |
37 | |
38 | /* 3 input pixels -> 8 composite samples */ |
39 | pixel_info_t const snes_ntsc_pixels [alignment_count] = { |
40 | { PIXEL_OFFSET( -4, -9 ), { 1, 1, .6667f, 0 } }, |
41 | { PIXEL_OFFSET( -2, -7 ), { .3333f, 1, 1, .3333f } }, |
42 | { PIXEL_OFFSET( 0, -5 ), { 0, .6667f, 1, 1 } }, |
43 | }; |
44 | |
45 | static void merge_kernel_fields( snes_ntsc_rgb_t* io ) |
46 | { |
47 | int n; |
48 | for ( n = burst_size; n; --n ) |
49 | { |
50 | snes_ntsc_rgb_t p0 = io [burst_size * 0] + rgb_bias; |
51 | snes_ntsc_rgb_t p1 = io [burst_size * 1] + rgb_bias; |
52 | snes_ntsc_rgb_t p2 = io [burst_size * 2] + rgb_bias; |
53 | /* merge colors without losing precision */ |
54 | io [burst_size * 0] = |
55 | ((p0 + p1 - ((p0 ^ p1) & snes_ntsc_rgb_builder)) >> 1) - rgb_bias; |
56 | io [burst_size * 1] = |
57 | ((p1 + p2 - ((p1 ^ p2) & snes_ntsc_rgb_builder)) >> 1) - rgb_bias; |
58 | io [burst_size * 2] = |
59 | ((p2 + p0 - ((p2 ^ p0) & snes_ntsc_rgb_builder)) >> 1) - rgb_bias; |
60 | ++io; |
61 | } |
62 | } |
63 | |
64 | static void correct_errors( snes_ntsc_rgb_t color, snes_ntsc_rgb_t* out ) |
65 | { |
66 | int n; |
67 | for ( n = burst_count; n; --n ) |
68 | { |
69 | unsigned i; |
70 | for ( i = 0; i < rgb_kernel_size / 2; i++ ) |
71 | { |
72 | snes_ntsc_rgb_t error = color - |
73 | out [i ] - out [(i+12)%14+14] - out [(i+10)%14+28] - |
74 | out [i + 7] - out [i + 5 +14] - out [i + 3 +28]; |
75 | DISTRIBUTE_ERROR( i+3+28, i+5+14, i+7 ); |
76 | } |
77 | out += alignment_count * rgb_kernel_size; |
78 | } |
79 | } |
80 | |
81 | void snes_ntsc_init( snes_ntsc_t* ntsc, snes_ntsc_setup_t const* setup ) |
82 | { |
83 | int merge_fields; |
84 | int entry; |
85 | init_t impl; |
86 | if ( !setup ) |
87 | setup = &snes_ntsc_composite; |
88 | init( &impl, setup ); |
89 | |
90 | merge_fields = setup->merge_fields; |
91 | if ( setup->artifacts <= -1 && setup->fringing <= -1 ) |
92 | merge_fields = 1; |
93 | |
94 | for ( entry = 0; entry < snes_ntsc_palette_size; entry++ ) |
95 | { |
96 | /* Reduce number of significant bits of source color. Clearing the |
97 | low bits of R and B were least notictable. Modifying green was too |
98 | noticeable. */ |
99 | int ir = entry >> 8 & 0x1E; |
100 | int ig = entry >> 4 & 0x1F; |
101 | int ib = entry << 1 & 0x1E; |
102 | |
103 | #if SNES_NTSC_BSNES_COLORTBL |
104 | if ( setup->bsnes_colortbl ) |
105 | { |
106 | int bgr15 = (ib << 10) | (ig << 5) | ir; |
107 | unsigned long rgb16 = setup->bsnes_colortbl [bgr15]; |
108 | ir = rgb16 >> 11 & 0x1E; |
109 | ig = rgb16 >> 6 & 0x1F; |
110 | ib = rgb16 & 0x1E; |
111 | } |
112 | #endif |
113 | |
114 | { |
115 | float rr = impl.to_float [ir]; |
116 | float gg = impl.to_float [ig]; |
117 | float bb = impl.to_float [ib]; |
118 | |
119 | float y, i, q = RGB_TO_YIQ( rr, gg, bb, y, i ); |
120 | |
121 | int r, g, b = YIQ_TO_RGB( y, i, q, impl.to_rgb, int, r, g ); |
122 | snes_ntsc_rgb_t rgb = PACK_RGB( r, g, b ); |
123 | |
124 | snes_ntsc_rgb_t* out = ntsc->table [entry]; |
125 | gen_kernel( &impl, y, i, q, out ); |
126 | if ( merge_fields ) |
127 | merge_kernel_fields( out ); |
128 | correct_errors( rgb, out ); |
129 | } |
130 | } |
131 | } |
132 | |
133 | #ifndef SNES_NTSC_NO_BLITTERS |
134 | |
135 | void snes_ntsc_blit( snes_ntsc_t const* ntsc, SNES_NTSC_IN_T const* input, long in_row_width, |
136 | int burst_phase, int in_width, int in_height, void* rgb_out, long out_pitch ) |
137 | { |
138 | int chunk_count = (in_width - 1) / snes_ntsc_in_chunk; |
139 | for ( ; in_height; --in_height ) |
140 | { |
141 | SNES_NTSC_IN_T const* line_in = input; |
142 | SNES_NTSC_BEGIN_ROW( ntsc, burst_phase, |
143 | snes_ntsc_black, snes_ntsc_black, SNES_NTSC_ADJ_IN( *line_in ) ); |
144 | snes_ntsc_out_t* restrict line_out = (snes_ntsc_out_t*) rgb_out; |
145 | int n; |
146 | ++line_in; |
147 | |
148 | for ( n = chunk_count; n; --n ) |
149 | { |
150 | /* order of input and output pixels must not be altered */ |
151 | SNES_NTSC_COLOR_IN( 0, SNES_NTSC_ADJ_IN( line_in [0] ) ); |
152 | SNES_NTSC_RGB_OUT( 0, line_out [0], SNES_NTSC_OUT_DEPTH ); |
153 | SNES_NTSC_RGB_OUT( 1, line_out [1], SNES_NTSC_OUT_DEPTH ); |
154 | |
155 | SNES_NTSC_COLOR_IN( 1, SNES_NTSC_ADJ_IN( line_in [1] ) ); |
156 | SNES_NTSC_RGB_OUT( 2, line_out [2], SNES_NTSC_OUT_DEPTH ); |
157 | SNES_NTSC_RGB_OUT( 3, line_out [3], SNES_NTSC_OUT_DEPTH ); |
158 | |
159 | SNES_NTSC_COLOR_IN( 2, SNES_NTSC_ADJ_IN( line_in [2] ) ); |
160 | SNES_NTSC_RGB_OUT( 4, line_out [4], SNES_NTSC_OUT_DEPTH ); |
161 | SNES_NTSC_RGB_OUT( 5, line_out [5], SNES_NTSC_OUT_DEPTH ); |
162 | SNES_NTSC_RGB_OUT( 6, line_out [6], SNES_NTSC_OUT_DEPTH ); |
163 | |
164 | line_in += 3; |
165 | line_out += 7; |
166 | } |
167 | |
168 | /* finish final pixels */ |
169 | SNES_NTSC_COLOR_IN( 0, snes_ntsc_black ); |
170 | SNES_NTSC_RGB_OUT( 0, line_out [0], SNES_NTSC_OUT_DEPTH ); |
171 | SNES_NTSC_RGB_OUT( 1, line_out [1], SNES_NTSC_OUT_DEPTH ); |
172 | |
173 | SNES_NTSC_COLOR_IN( 1, snes_ntsc_black ); |
174 | SNES_NTSC_RGB_OUT( 2, line_out [2], SNES_NTSC_OUT_DEPTH ); |
175 | SNES_NTSC_RGB_OUT( 3, line_out [3], SNES_NTSC_OUT_DEPTH ); |
176 | |
177 | SNES_NTSC_COLOR_IN( 2, snes_ntsc_black ); |
178 | SNES_NTSC_RGB_OUT( 4, line_out [4], SNES_NTSC_OUT_DEPTH ); |
179 | SNES_NTSC_RGB_OUT( 5, line_out [5], SNES_NTSC_OUT_DEPTH ); |
180 | SNES_NTSC_RGB_OUT( 6, line_out [6], SNES_NTSC_OUT_DEPTH ); |
181 | |
182 | burst_phase = (burst_phase + 1) % snes_ntsc_burst_count; |
183 | input += in_row_width; |
184 | rgb_out = (char*) rgb_out + out_pitch; |
185 | } |
186 | } |
187 | |
188 | void snes_ntsc_blit_hires( snes_ntsc_t const* ntsc, SNES_NTSC_IN_T const* input, long in_row_width, |
189 | int burst_phase, int in_width, int in_height, void* rgb_out, long out_pitch ) |
190 | { |
191 | int chunk_count = (in_width - 2) / (snes_ntsc_in_chunk * 2); |
192 | for ( ; in_height; --in_height ) |
193 | { |
194 | SNES_NTSC_IN_T const* line_in = input; |
195 | SNES_NTSC_HIRES_ROW( ntsc, burst_phase, |
196 | snes_ntsc_black, snes_ntsc_black, snes_ntsc_black, |
197 | SNES_NTSC_ADJ_IN( line_in [0] ), |
198 | SNES_NTSC_ADJ_IN( line_in [1] ) ); |
199 | snes_ntsc_out_t* restrict line_out = (snes_ntsc_out_t*) rgb_out; |
200 | int n; |
201 | line_in += 2; |
202 | |
203 | for ( n = chunk_count; n; --n ) |
204 | { |
205 | /* twice as many input pixels per chunk */ |
206 | SNES_NTSC_COLOR_IN( 0, SNES_NTSC_ADJ_IN( line_in [0] ) ); |
207 | SNES_NTSC_HIRES_OUT( 0, line_out [0], SNES_NTSC_OUT_DEPTH ); |
208 | |
209 | SNES_NTSC_COLOR_IN( 1, SNES_NTSC_ADJ_IN( line_in [1] ) ); |
210 | SNES_NTSC_HIRES_OUT( 1, line_out [1], SNES_NTSC_OUT_DEPTH ); |
211 | |
212 | SNES_NTSC_COLOR_IN( 2, SNES_NTSC_ADJ_IN( line_in [2] ) ); |
213 | SNES_NTSC_HIRES_OUT( 2, line_out [2], SNES_NTSC_OUT_DEPTH ); |
214 | |
215 | SNES_NTSC_COLOR_IN( 3, SNES_NTSC_ADJ_IN( line_in [3] ) ); |
216 | SNES_NTSC_HIRES_OUT( 3, line_out [3], SNES_NTSC_OUT_DEPTH ); |
217 | |
218 | SNES_NTSC_COLOR_IN( 4, SNES_NTSC_ADJ_IN( line_in [4] ) ); |
219 | SNES_NTSC_HIRES_OUT( 4, line_out [4], SNES_NTSC_OUT_DEPTH ); |
220 | |
221 | SNES_NTSC_COLOR_IN( 5, SNES_NTSC_ADJ_IN( line_in [5] ) ); |
222 | SNES_NTSC_HIRES_OUT( 5, line_out [5], SNES_NTSC_OUT_DEPTH ); |
223 | SNES_NTSC_HIRES_OUT( 6, line_out [6], SNES_NTSC_OUT_DEPTH ); |
224 | |
225 | line_in += 6; |
226 | line_out += 7; |
227 | } |
228 | |
229 | SNES_NTSC_COLOR_IN( 0, snes_ntsc_black ); |
230 | SNES_NTSC_HIRES_OUT( 0, line_out [0], SNES_NTSC_OUT_DEPTH ); |
231 | |
232 | SNES_NTSC_COLOR_IN( 1, snes_ntsc_black ); |
233 | SNES_NTSC_HIRES_OUT( 1, line_out [1], SNES_NTSC_OUT_DEPTH ); |
234 | |
235 | SNES_NTSC_COLOR_IN( 2, snes_ntsc_black ); |
236 | SNES_NTSC_HIRES_OUT( 2, line_out [2], SNES_NTSC_OUT_DEPTH ); |
237 | |
238 | SNES_NTSC_COLOR_IN( 3, snes_ntsc_black ); |
239 | SNES_NTSC_HIRES_OUT( 3, line_out [3], SNES_NTSC_OUT_DEPTH ); |
240 | |
241 | SNES_NTSC_COLOR_IN( 4, snes_ntsc_black ); |
242 | SNES_NTSC_HIRES_OUT( 4, line_out [4], SNES_NTSC_OUT_DEPTH ); |
243 | |
244 | SNES_NTSC_COLOR_IN( 5, snes_ntsc_black ); |
245 | SNES_NTSC_HIRES_OUT( 5, line_out [5], SNES_NTSC_OUT_DEPTH ); |
246 | SNES_NTSC_HIRES_OUT( 6, line_out [6], SNES_NTSC_OUT_DEPTH ); |
247 | |
248 | burst_phase = (burst_phase + 1) % snes_ntsc_burst_count; |
249 | input += in_row_width; |
250 | rgb_out = (char*) rgb_out + out_pitch; |
251 | } |
252 | } |
253 | |
254 | /* 12.5% scanlines like in snes_ntsc example instead of zsnes's 25% */ |
255 | #define PIXEL_OUT( x ) \ |
256 | SNES_NTSC_RGB_OUT( x, value, SNES_NTSC_OUT_DEPTH ); \ |
257 | line_outa[x] = value; \ |
258 | line_outb[x] = value - (value >> snes_ntsc_scanline_offset & snes_ntsc_scanline_mask); |
259 | |
260 | void snes_ntsc_blit_scanlines( snes_ntsc_t const* ntsc, SNES_NTSC_IN_T const* input, long in_row_width, |
261 | int burst_phase, int in_width, int in_height, void* rgb_out, long out_pitch ) |
262 | { |
263 | unsigned value; |
264 | int chunk_count = (in_width - 1) / snes_ntsc_in_chunk; |
265 | for ( ; in_height; --in_height ) |
266 | { |
267 | SNES_NTSC_IN_T const* line_in = input; |
268 | SNES_NTSC_BEGIN_ROW( ntsc, burst_phase, |
269 | snes_ntsc_black, snes_ntsc_black, SNES_NTSC_ADJ_IN( *line_in ) ); |
270 | snes_ntsc_out_t * restrict line_outa = (snes_ntsc_out_t *) rgb_out; |
271 | snes_ntsc_out_t * restrict line_outb = (snes_ntsc_out_t *) ((char *) line_outa + out_pitch); |
272 | int n; |
273 | ++line_in; |
274 | |
275 | for ( n = chunk_count; n; --n ) |
276 | { |
277 | /* order of input and output pixels must not be altered */ |
278 | SNES_NTSC_COLOR_IN( 0, SNES_NTSC_ADJ_IN( line_in [0] ) ); |
279 | PIXEL_OUT (0); |
280 | PIXEL_OUT (1); |
281 | |
282 | SNES_NTSC_COLOR_IN( 1, SNES_NTSC_ADJ_IN( line_in [1] ) ); |
283 | PIXEL_OUT (2); |
284 | PIXEL_OUT (3); |
285 | |
286 | SNES_NTSC_COLOR_IN( 2, SNES_NTSC_ADJ_IN( line_in [2] ) ); |
287 | PIXEL_OUT (4); |
288 | PIXEL_OUT (5); |
289 | PIXEL_OUT (6); |
290 | |
291 | line_in += 3; |
292 | line_outa += 7; |
293 | line_outb += 7; |
294 | } |
295 | |
296 | /* finish final pixels */ |
297 | SNES_NTSC_COLOR_IN( 0, snes_ntsc_black ); |
298 | PIXEL_OUT (0); |
299 | PIXEL_OUT (1); |
300 | |
301 | SNES_NTSC_COLOR_IN( 1, snes_ntsc_black ); |
302 | PIXEL_OUT (2); |
303 | PIXEL_OUT (3); |
304 | |
305 | SNES_NTSC_COLOR_IN( 2, snes_ntsc_black ); |
306 | PIXEL_OUT (4); |
307 | PIXEL_OUT (5); |
308 | PIXEL_OUT (6); |
309 | |
310 | burst_phase = (burst_phase + 1) % snes_ntsc_burst_count; |
311 | input += in_row_width; |
312 | rgb_out = (char*) rgb_out + 2 * out_pitch; |
313 | } |
314 | } |
315 | |
316 | #define PIXEL_OUT_HIRES( x ) \ |
317 | SNES_NTSC_HIRES_OUT( x, value, SNES_NTSC_OUT_DEPTH ); \ |
318 | line_outa[x] = value; \ |
319 | line_outb[x] = value - (value >> snes_ntsc_scanline_offset & snes_ntsc_scanline_mask); |
320 | |
321 | void snes_ntsc_blit_hires_scanlines( snes_ntsc_t const* ntsc, SNES_NTSC_IN_T const* input, long in_row_width, |
322 | int burst_phase, int in_width, int in_height, void* rgb_out, long out_pitch ) |
323 | { |
324 | int chunk_count = (in_width - 2) / (snes_ntsc_in_chunk * 2); |
325 | unsigned value; |
326 | for ( ; in_height; --in_height ) |
327 | { |
328 | SNES_NTSC_IN_T const* line_in = input; |
329 | SNES_NTSC_HIRES_ROW( ntsc, burst_phase, |
330 | snes_ntsc_black, snes_ntsc_black, snes_ntsc_black, |
331 | SNES_NTSC_ADJ_IN( line_in [0] ), |
332 | SNES_NTSC_ADJ_IN( line_in [1] ) ); |
333 | snes_ntsc_out_t* restrict line_outa = (snes_ntsc_out_t*) rgb_out; |
334 | snes_ntsc_out_t* restrict line_outb = (snes_ntsc_out_t*) ((char *) line_outa + out_pitch); |
335 | int n; |
336 | line_in += 2; |
337 | |
338 | for ( n = chunk_count; n; --n ) |
339 | { |
340 | /* twice as many input pixels per chunk */ |
341 | SNES_NTSC_COLOR_IN( 0, SNES_NTSC_ADJ_IN( line_in [0] ) ); |
342 | PIXEL_OUT_HIRES (0); |
343 | |
344 | SNES_NTSC_COLOR_IN( 1, SNES_NTSC_ADJ_IN( line_in [1] ) ); |
345 | PIXEL_OUT_HIRES (1); |
346 | |
347 | SNES_NTSC_COLOR_IN( 2, SNES_NTSC_ADJ_IN( line_in [2] ) ); |
348 | PIXEL_OUT_HIRES (2); |
349 | |
350 | SNES_NTSC_COLOR_IN( 3, SNES_NTSC_ADJ_IN( line_in [3] ) ); |
351 | PIXEL_OUT_HIRES (3); |
352 | |
353 | SNES_NTSC_COLOR_IN( 4, SNES_NTSC_ADJ_IN( line_in [4] ) ); |
354 | PIXEL_OUT_HIRES (4); |
355 | |
356 | SNES_NTSC_COLOR_IN( 5, SNES_NTSC_ADJ_IN( line_in [5] ) ); |
357 | PIXEL_OUT_HIRES (5); |
358 | PIXEL_OUT_HIRES (6); |
359 | |
360 | line_in += 6; |
361 | line_outa += 7; |
362 | line_outb += 7; |
363 | } |
364 | |
365 | SNES_NTSC_COLOR_IN( 0, snes_ntsc_black ); |
366 | PIXEL_OUT_HIRES (0); |
367 | |
368 | SNES_NTSC_COLOR_IN( 1, snes_ntsc_black ); |
369 | PIXEL_OUT_HIRES (1); |
370 | |
371 | SNES_NTSC_COLOR_IN( 2, snes_ntsc_black ); |
372 | PIXEL_OUT_HIRES (2); |
373 | |
374 | SNES_NTSC_COLOR_IN( 3, snes_ntsc_black ); |
375 | PIXEL_OUT_HIRES (3); |
376 | |
377 | SNES_NTSC_COLOR_IN( 4, snes_ntsc_black ); |
378 | PIXEL_OUT_HIRES (4); |
379 | |
380 | SNES_NTSC_COLOR_IN( 5, snes_ntsc_black ); |
381 | PIXEL_OUT_HIRES (5); |
382 | PIXEL_OUT_HIRES (6); |
383 | |
384 | burst_phase = (burst_phase + 1) % snes_ntsc_burst_count; |
385 | input += in_row_width; |
386 | rgb_out = (char*) rgb_out + out_pitch * 2; |
387 | } |
388 | } |
389 | |
390 | #endif |
391 | |