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 6can redistribute it and/or modify it under the terms of the GNU Lesser 7General Public License as published by the Free Software Foundation; either 8version 2.1 of the License, or (at your option) any later version. This 9module is distributed in the hope that it will be useful, but WITHOUT ANY 10WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 11FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 12details. You should have received a copy of the GNU Lesser General Public 13License along with this module; if not, write to the Free Software Foundation, 14Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ 15 16nes_ntsc_setup_t const nes_ntsc_monochrome = { 0,-1, 0, 0,.2, 0,.2,-.2,-.2,-1, 1, 0, 0, 0, 0 }; 17nes_ntsc_setup_t const nes_ntsc_composite = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0 }; 18nes_ntsc_setup_t const nes_ntsc_svideo = { 0, 0, 0, 0,.2, 0,.2, -1, -1, 0, 1, 0, 0, 0, 0 }; 19nes_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 */ 35pixel_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 41static 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 60static 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 77void 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 236void nes_ntsc_blit( nes_ntsc_t const* ntsc, NES_NTSC_IN_T const* input, 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_BEGIN_ROW( ntsc, burst_phase, 244 nes_ntsc_black, nes_ntsc_black, NES_NTSC_ADJ_IN( *line_in ) ); 245 nes_ntsc_out_t* restrict line_out = (nes_ntsc_out_t*) rgb_out; 246 int n; 247 ++line_in; 248 249 for ( n = chunk_count; n; --n ) 250 { 251 /* order of input and output pixels must not be altered */ 252 NES_NTSC_COLOR_IN( 0, NES_NTSC_ADJ_IN( line_in [0] ) ); 253 NES_NTSC_RGB_OUT( 0, line_out [0], NES_NTSC_OUT_DEPTH ); 254 NES_NTSC_RGB_OUT( 1, line_out [1], NES_NTSC_OUT_DEPTH ); 255 256 NES_NTSC_COLOR_IN( 1, NES_NTSC_ADJ_IN( line_in [1] ) ); 257 NES_NTSC_RGB_OUT( 2, line_out [2], NES_NTSC_OUT_DEPTH ); 258 NES_NTSC_RGB_OUT( 3, line_out [3], NES_NTSC_OUT_DEPTH ); 259 260 NES_NTSC_COLOR_IN( 2, NES_NTSC_ADJ_IN( line_in [2] ) ); 261 NES_NTSC_RGB_OUT( 4, line_out [4], NES_NTSC_OUT_DEPTH ); 262 NES_NTSC_RGB_OUT( 5, line_out [5], NES_NTSC_OUT_DEPTH ); 263 NES_NTSC_RGB_OUT( 6, line_out [6], NES_NTSC_OUT_DEPTH ); 264 265 line_in += 3; 266 line_out += 7; 267 } 268 269 /* finish final pixels */ 270 NES_NTSC_COLOR_IN( 0, nes_ntsc_black ); 271 NES_NTSC_RGB_OUT( 0, line_out [0], NES_NTSC_OUT_DEPTH ); 272 NES_NTSC_RGB_OUT( 1, line_out [1], NES_NTSC_OUT_DEPTH ); 273 274 NES_NTSC_COLOR_IN( 1, nes_ntsc_black ); 275 NES_NTSC_RGB_OUT( 2, line_out [2], NES_NTSC_OUT_DEPTH ); 276 NES_NTSC_RGB_OUT( 3, line_out [3], NES_NTSC_OUT_DEPTH ); 277 278 NES_NTSC_COLOR_IN( 2, nes_ntsc_black ); 279 NES_NTSC_RGB_OUT( 4, line_out [4], NES_NTSC_OUT_DEPTH ); 280 NES_NTSC_RGB_OUT( 5, line_out [5], NES_NTSC_OUT_DEPTH ); 281 NES_NTSC_RGB_OUT( 6, line_out [6], NES_NTSC_OUT_DEPTH ); 282 283 burst_phase = (burst_phase + 1) % nes_ntsc_burst_count; 284 input += in_row_width; 285 rgb_out = (char*) rgb_out + out_pitch; 286 } 287} 288 289#endif 290