Second pass at Blargg NTSC filtering. This code comes directly from the

latest Atari800 drop, and is the most recent version I could find.  As
with the last commit, it still doesn't actually do anything (but it does
compile).


git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@2395 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
This commit is contained in:
stephena 2012-02-26 18:58:41 +00:00
parent 6836963e8b
commit 5d4c09da54
6 changed files with 1417 additions and 478 deletions

View File

@ -25,28 +25,11 @@
#include <cmath> #include <cmath>
#include <cstdlib> #include <cstdlib>
static float const rgb_unit = 0x1000;
static float const pi = 3.14159265358979323846f;
/* important to use + and not | since values are signed */
#define MAKE_KRGB( r, g, b ) \
( ((r + 16) >> 5 << 20) + ((g + 16) >> 5 << 10) + ((b + 16) >> 5) )
#define MAKE_KMASK( x ) (((x) << 20) | ((x) << 10) | (x))
/* clamp each RGB component to 0 to 0x7F range (low two bits are trashed) */
#define CLAMP_RGB( io, adj ) {\
ntsc_rgb_t sub = (io) >> (7 + adj) & MAKE_KMASK( 3 );\
ntsc_rgb_t clamp = MAKE_KMASK( 0x202 ) - sub;\
io = ((io) | clamp) & (clamp - sub);\
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NTSCFilter::NTSCFilter() NTSCFilter::NTSCFilter()
: myNTSCEmu(NULL), : mySetup(atari_ntsc_composite),
myCurrentModeNum(-1) myCurrentModeNum(0)
{ {
// Add various presets
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -55,398 +38,255 @@ NTSCFilter::~NTSCFilter()
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const string& NTSCFilter::next() void NTSCFilter::updateFilter()
{ {
double yiq_table[768];
return EmptyString; updateYIQTable(yiq_table, mySetup.burst_phase * M_PI);
/* The gamma setting is not used in atari_ntsc (palette generation is
placed in another module), so below we do not set the gamma field
of the mySetup structure. */
// According to how Atari800 defines 'external', we're using an external
// palette (since it is generated outside of the COLOUR_NTSC routines)
/* External palette must not be adjusted, so FILTER_NTSC
settings are set to defaults so they don't change the source
palette in any way. */
mySetup.hue = 0.0;
mySetup.saturation = 0.0;
mySetup.contrast = 0.0;
mySetup.brightness = 0.0;
mySetup.yiq_palette = yiq_table;
atari_ntsc_init(&myFilter, &mySetup);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void NTSCFilter::atari_ntsc_init(atari_ntsc_setup_t const* setup) void NTSCFilter::restoreDefaults()
{ {
/* init pixel renderer */ mySetup = atari_ntsc_composite;
int entry;
float burst_phase = (setup->burst_phase) * pi;
ntsc_to_rgb_t ntsc;
ntsc_to_rgb_init( &ntsc, setup, setup->hue * pi - burst_phase );
for ( entry = 0; entry < atari_ntsc_color_count; entry++ )
{
/*NTSC PHASE CONSTANTS.*/
float ntsc_phase_constant[16]={0,2.27,1.87,1.62,
1.22,0.62,-0.31,-0.855,
-1.18,-1.43,-1.63,-1.93,
-2.38,-3.43,2.52,2.07};
/*NTSC luma multipliers from gtia.pdf*/
float luma_mult[16]={
0.6941, 0.7091, 0.7241, 0.7401,
0.7560, 0.7741, 0.7931, 0.8121,
0.8260, 0.8470, 0.8700, 0.8930,
0.9160, 0.9420, 0.9690, 1.0000};
/* calculate yiq for color entry */
int color = entry >> 4;
float angle = burst_phase + ntsc_phase_constant[color];
float lumafactor = ( luma_mult[ entry & 0x0f] - luma_mult[0] )/( luma_mult[15] - luma_mult[0] );
float adj_lumafactor = pow(lumafactor, 1 +setup->gamma_adj);
float y = adj_lumafactor * rgb_unit;
float base_saturation = (color ? rgb_unit * 0.35f: 0.0f);
float saturation_ramp = setup->saturation_ramp;
float saturation = base_saturation * ( 1 - saturation_ramp*lumafactor );
float i = sin( angle ) * saturation;
float q = cos( angle ) * saturation;
ntsc_rgb_t* out = myNTSCEmu->table [entry];
y = y * ntsc.contrast + ntsc.brightness;
/* generate at four alignments with respect to output */
ntsc.composite [composite_border + 0] = i + y;
ntsc.composite [composite_border + 1] = q + y;
out = gen_pixel( &ntsc, 0, 0, out );
ntsc.composite [composite_border + 0] = 0;
ntsc.composite [composite_border + 1] = 0;
ntsc.composite [composite_border + 2] = i - y;
ntsc.composite [composite_border + 3] = q - y;
out = gen_pixel( &ntsc, 2, 2, out );
ntsc.composite [composite_border + 2] = 0;
ntsc.composite [composite_border + 3] = 0;
ntsc.composite [composite_border + 4] = i + y;
ntsc.composite [composite_border + 5] = q + y;
out = gen_pixel( &ntsc, 4, 4, out );
ntsc.composite [composite_border + 4] = 0;
ntsc.composite [composite_border + 5] = 0;
ntsc.composite [composite_border + 6] = i - y;
ntsc.composite [composite_border + 7] = q - y;
out = gen_pixel( &ntsc, 6, 6, out );
ntsc.composite [composite_border + 6] = 0;
ntsc.composite [composite_border + 7] = 0;
/* correct roundoff errors that would cause vertical bands in solid areas */
{
float r = y + i * ntsc.to_rgb [0] + q * ntsc.to_rgb [1];
float g = y + i * ntsc.to_rgb [2] + q * ntsc.to_rgb [3];
float b = y + i * ntsc.to_rgb [4] + q * ntsc.to_rgb [5];
ntsc_rgb_t correct = MAKE_KRGB( (int) r, (int) g, (int) b ) + MAKE_KMASK( 0x100 );
int i;
out = myNTSCEmu->table [entry];
for ( i = 0; i < rgb_kernel_size / 2; i++ )
{
/* sum as would occur when outputting run of pixels using same color,
but don't sum first kernel; the difference between this and the correct
color is what the first kernel's entry should be */
ntsc_rgb_t sum = out [(i+12)%14+14]+out[(i+10)%14+28]+out[(i+8)%14+42]+
out[i+7]+out [ i+ 5 +14]+out[ i+ 3 +28]+out[ i+1 +42];
out [i] = correct - sum;
}
}
}
//FIXME Log_print("atari_ntsc_init(): sharpness:%f saturation:%f brightness:%f contrast:%f gaussian_factor:%f burst_phase:%f, hue:%f gamma_adj:%f saturation_ramp:%f\n",setup->sharpness,setup->saturation,setup->brightness,setup->contrast,setup->gaussian_factor,setup->burst_phase,setup->hue,setup->gamma_adj,setup->saturation_ramp);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void NTSCFilter::atari_ntsc_blit( unsigned char const* in, long in_pitch, void NTSCFilter::setPreset(int preset)
int width, int height, unsigned short* out, long out_pitch )
{ {
int const chunk_count = (width - 10) / 7; if (preset < PRESET_CUSTOM) {
long next_in_line = in_pitch - chunk_count * 4; mySetup = *presets[preset];
long next_out_line = out_pitch - (chunk_count + 1) * (7 * sizeof *out); updateFilter();
while ( height-- )
{ #if 0 // FIXME - what are these items for??
#define ENTRY( n ) myNTSCEmu->table [n] /* Copy settings from the preset to NTSC setup. */
ntsc_rgb_t const* k1 = ENTRY( 0 ); COLOURS_NTSC_specific_setup.hue = mySetup.hue;
ntsc_rgb_t const* k2 = k1; COLOURS_NTSC_setup.saturation = mySetup.saturation;
ntsc_rgb_t const* k3 = k1; COLOURS_NTSC_setup.contrast = mySetup.contrast;
ntsc_rgb_t const* k4 = k1; COLOURS_NTSC_setup.brightness = mySetup.brightness;
ntsc_rgb_t const* k5 = k4; COLOURS_NTSC_setup.gamma = mySetup.gamma;
ntsc_rgb_t const* k6 = k4; #endif
ntsc_rgb_t const* k7 = k4;
int n;
#if ATARI_NTSC_RGB_BITS == 16
#define TO_RGB( in ) ((in >> 11 & 0xF800) | (in >> 6 & 0x07C0) | (in >> 2 & 0x001F))
#elif ATARI_NTSC_RGB_BITS == 15
#define TO_RGB( in ) ((in >> 12 & 0x7C00) | (in >> 7 & 0x03E0) | (in >> 2 & 0x001F))
#endif
#define PIXEL( a ) {\
ntsc_rgb_t temp =\
k0 [a ] + k1 [(a+5)%7+14] + k2 [(a+3)%7+28] + k3 [(a+1)%7+42] +\
k4 [a+7] + k5 [(a+5)%7+21] + k6 [(a+3)%7+35] + k7 [(a+1)%7+49];\
if ( a ) out [a-1] = rgb;\
CLAMP_RGB( temp, 0 );\
rgb = TO_RGB( temp );\
}
for ( n = chunk_count; n; --n )
{
ntsc_rgb_t const* k0 = ENTRY( in [0] );
int rgb;
PIXEL( 0 );
PIXEL( 1 );
k5 = k1;
k1 = ENTRY( in [1] );
PIXEL( 2 );
PIXEL( 3 );
k6 = k2;
k2 = ENTRY( in [2] );
PIXEL( 4 );
PIXEL( 5 );
k7 = k3;
k3 = ENTRY( in [3] );
PIXEL( 6 );
out [6] = rgb;
k4 = k0;
in += 4;
out += 7;
}
{
ntsc_rgb_t const* k0 = ENTRY( 0 );
int rgb;
PIXEL( 0 );
PIXEL( 1 );
k5 = k1;
k1 = k0;
PIXEL( 2 );
PIXEL( 3 );
k6 = k2;
k2 = k0;
PIXEL( 4 );
PIXEL( 5 );
k7 = k3;
k3 = k0;
PIXEL( 6 );
k4 = k0;
out [6] = rgb;
out += 7;
PIXEL( 0 );
PIXEL( 1 );
k5 = k0;
PIXEL( 2 );
out [2] = rgb;
}
#undef PIXEL
in += next_in_line;
out = (unsigned short*) ((char*) out + next_out_line);
} }
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void NTSCFilter::rotate_matrix( float const* in, float s, float c, float* out ) int NTSCFilter::getPreset()
{ {
int n = 3; // FIXME - for now just return composite
while ( n-- ) return 0;
{ #if 0
float i = *in++;
float q = *in++;
*out++ = i * c - q * s;
*out++ = i * s + q * c;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void NTSCFilter::ntsc_to_rgb_init( ntsc_to_rgb_t* ntsc, atari_ntsc_setup_t const* setup, float hue )
{
static float const to_rgb [6] = { 0.956, 0.621, -0.272, -0.647, -1.105, 1.702 };
float gaussian_factor = 1+setup->gaussian_factor; /* 1 = normal, > 1 reduces echoes of bright objects (was 2.1)*/
float const brightness_bias = rgb_unit / 128; /* reduces vert bands on artifact colored areas */
int i; int i;
/* ranges need to be scaled a bit to avoid pixels overflowing at extremes */
ntsc->brightness = (setup->brightness-0.0f) * (0.4f * rgb_unit) + brightness_bias; for (i = 0; i < PRESET_SIZE; i ++) {
ntsc->contrast = (setup->contrast-0.0f) * 0.4f + 1; if (Util_almostequal(mySetup.sharpness, presets[i]->sharpness, 0.001) &&
ntsc->sharpness = 1 + (setup->sharpness < 0 ? setup->sharpness * 0.5f : setup->sharpness); Util_almostequal(mySetup.resolution, presets[i]->resolution, 0.001) &&
Util_almostequal(mySetup.artifacts, presets[i]->artifacts, 0.001) &&
for ( i = 0; i < composite_size; i++ ) Util_almostequal(mySetup.fringing, presets[i]->fringing, 0.001) &&
ntsc->composite [i] = 0; Util_almostequal(mySetup.bleed, presets[i]->bleed, 0.001) &&
Util_almostequal(mySetup.burst_phase, presets[i]->burst_phase, 0.001) &&
/* Generate gaussian kernel, padded with zero */ Util_almostequal(COLOURS_NTSC_specific_setup.hue, presets[i]->hue, 0.001) &&
for ( i = 0; i < ntsc_kernel_size; i++ ) Util_almostequal(COLOURS_NTSC_setup.saturation, presets[i]->saturation, 0.001) &&
ntsc->kernel [i] = 0; Util_almostequal(COLOURS_NTSC_setup.contrast, presets[i]->contrast, 0.001) &&
for ( i = -composite_border; i <= composite_border; i++ ) Util_almostequal(COLOURS_NTSC_setup.brightness, presets[i]->brightness, 0.001) &&
ntsc->kernel [ntsc_kernel_size / 2 + i] = exp( i * i * (-0.03125f * gaussian_factor) ); Util_almostequal(COLOURS_NTSC_setup.gamma, presets[i]->gamma, 0.001))
return i;
/* normalize kernel totals of every fourth sample (at all four phases) to 0.5, otherwise */
/* i/q low-pass will favor one of the four alignments and cause repeating spots */
for ( i = 0; i < 4; i++ )
{
double sum = 0;
float scale;
int x;
for ( x = i; x < ntsc_kernel_size; x += 4 )
sum += ntsc->kernel [x];
scale = 0.5 / sum;
for ( x = i; x < ntsc_kernel_size; x += 4 )
ntsc->kernel [x] *= scale;
} }
return PRESET_CUSTOM;
/* adjust decoder matrix */ #endif
{
float sat = setup->saturation + 1;
rotate_matrix( to_rgb, sin( hue ) * sat, cos( hue ) * sat, ntsc->to_rgb );
}
memset( ntsc->rgb, 0, sizeof ntsc->rgb );
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void NTSCFilter::ntsc_to_rgb( ntsc_to_rgb_t const* ntsc, int offset, short* out ) void NTSCFilter::nextPreset()
{ {
float const* kernel = &ntsc->kernel [ntsc_kernel_size / 2 - offset]; int preset = getPreset();
float f0 = ntsc->composite [offset];
float f1 = ntsc->composite [offset + 1]; if (preset == PRESET_CUSTOM)
float f2 = ntsc->composite [offset + 2]; preset = PRESET_COMPOSITE;
float f3 = ntsc->composite [offset + 3]; else
int x = 0; preset = (preset + 1) % PRESET_SIZE;
while ( x < composite_size ) setPreset(preset);
{ }
#define PIXEL( get_y ) \
{\ #if 0 // FIXME
float i = kernel [ 0] * f0 + kernel [-2] * f2;\ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
float q = kernel [-1] * f1 + kernel [-3] * f3;\ int NTSCFilter::FILTER_NTSC_ReadConfig(char *option, char *ptr)
float y = get_y;\ {
float r = y + i * ntsc->to_rgb [0] + q * ntsc->to_rgb [1];\ if (strcmp(option, "FILTER_NTSC_SHARPNESS") == 0)
float g = y + i * ntsc->to_rgb [2] + q * ntsc->to_rgb [3];\ return Util_sscandouble(ptr, &mySetup.sharpness);
float b = y + i * ntsc->to_rgb [4] + q * ntsc->to_rgb [5];\ else if (strcmp(option, "FILTER_NTSC_RESOLUTION") == 0)
kernel++;\ return Util_sscandouble(ptr, &mySetup.resolution);
out [0] = (int) r;\ else if (strcmp(option, "FILTER_NTSC_ARTIFACTS") == 0)
out [1] = (int) g;\ return Util_sscandouble(ptr, &mySetup.artifacts);
out [2] = (int) b;\ else if (strcmp(option, "FILTER_NTSC_FRINGING") == 0)
out += 3;\ return Util_sscandouble(ptr, &mySetup.fringing);
} else if (strcmp(option, "FILTER_NTSC_BLEED") == 0)
return Util_sscandouble(ptr, &mySetup.bleed);
PIXEL( i - ntsc->composite [x + 0] ) else if (strcmp(option, "FILTER_NTSC_BURST_PHASE") == 0)
PIXEL( q - ntsc->composite [x + 1] ) return Util_sscandouble(ptr, &mySetup.burst_phase);
PIXEL( ntsc->composite [x + 2] - i ) else
PIXEL( ntsc->composite [x + 3] - q ) return FALSE;
x += 4;
#undef PIXEL
}
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void NTSCFilter::rescale( short const* in, int count, short* out ) void NTSCFilter::FILTER_NTSC_WriteConfig(FILE *fp)
{ {
do fprintf(fp, "FILTER_NTSC_SHARPNESS=%g\n", mySetup.sharpness);
{ fprintf(fp, "FILTER_NTSC_RESOLUTION=%g\n", mySetup.resolution);
int const accuracy = 16; fprintf(fp, "FILTER_NTSC_ARTIFACTS=%g\n", mySetup.artifacts);
int const unit = 1 << accuracy; fprintf(fp, "FILTER_NTSC_FRINGING=%g\n", mySetup.fringing);
int const step = unit / 8; fprintf(fp, "FILTER_NTSC_BLEED=%g\n", mySetup.bleed);
int left = unit - step; fprintf(fp, "FILTER_NTSC_BURST_PHASE=%g\n", mySetup.burst_phase);
int right = step;
int n = 7;
while ( n-- )
{
int r = (in [0] * left + in [3] * right) >> accuracy;
int g = (in [1] * left + in [4] * right) >> accuracy;
int b = (in [2] * left + in [5] * right) >> accuracy;
*out++ = r;
*out++ = g;
*out++ = b;
left -= step;
right += step;
in += 3;
}
in += 3;
}
while ( (count -= 7) > 0 );
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void NTSCFilter::sharpen( short const* in, float level, int count, short* out ) int NTSCFilter::FILTER_NTSC_Initialise(int *argc, char *argv[])
{ {
/* to do: sharpen luma only? */ int i;
int const accuracy = 16; int j;
int const middle = (int) (level * (1 << accuracy));
int const side = (middle - (1 << accuracy)) >> 1;
*out++ = *in++;
*out++ = *in++;
*out++ = *in++;
for ( count = (count - 2) * 3; count--; in++ )
*out++ = (in [0] * middle - in [-3] * side - in [3] * side) >> accuracy;
*out++ = *in++;
*out++ = *in++;
*out++ = *in++;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NTSCFilter::ntsc_rgb_t* NTSCFilter::gen_pixel( ntsc_to_rgb_t* ntsc, int ntsc_pos, int rescaled_pos, ntsc_rgb_t* out )
{
ntsc_to_rgb( ntsc, composite_border + ntsc_pos, ntsc->rgb [rgb_pad] );
rescale( ntsc->rgb [0], rescaled_size, ntsc->rescaled [1] );
sharpen( ntsc->rescaled [1], ntsc->sharpness, rescaled_size, ntsc->rescaled [0] );
{
short const* in = ntsc->rescaled [rescaled_pos];
int n = rgb_kernel_size;
while ( n-- )
{
*out++ = MAKE_KRGB( in [0], in [1], in [2] );
in += 3;
}
}
return out;
}
#if 0 // FIXME - disabled for now
/* added for Atari800, by perrym*/
void ATARI_NTSC_DEFAULTS_Initialise(int *argc, char *argv[], atari_ntsc_setup_t *atari_ntsc_setup)
{
int i, j;
/* Adjust default values here */
atari_ntsc_setup->brightness = -0.1;
atari_ntsc_setup->sharpness = -0.5;
atari_ntsc_setup->saturation = -0.1;
atari_ntsc_setup->gamma_adj = -0.15;
atari_ntsc_setup->burst_phase = -0.60;
atari_ntsc_setup->saturation_ramp = 0.25;
for (i = j = 1; i < *argc; i++) { for (i = j = 1; i < *argc; i++) {
if (strcmp(argv[i], "-ntsc_hue") == 0) { int i_a = (i + 1 < *argc); /* is argument available? */
atari_ntsc_setup->hue = atof(argv[++i]); int a_m = FALSE; /* error, argument missing! */
}else if (strcmp(argv[i], "-ntsc_sat") == 0){
atari_ntsc_setup->saturation = atof(argv[++i]); if (strcmp(argv[i], "-ntsc-sharpness") == 0) {
}else if (strcmp(argv[i], "-ntsc_cont") == 0){ if (i_a)
atari_ntsc_setup->contrast = atof(argv[++i]); mySetup.sharpness = atof(argv[++i]);
}else if (strcmp(argv[i], "-ntsc_bright") == 0){ else a_m = TRUE;
atari_ntsc_setup->brightness = atof(argv[++i]); }
}else if (strcmp(argv[i], "-ntsc_sharp") == 0){ else if (strcmp(argv[i], "-ntsc-resolution") == 0) {
atari_ntsc_setup->sharpness = atof(argv[++i]); if (i_a)
}else if (strcmp(argv[i], "-ntsc_burst") == 0){ mySetup.resolution = atof(argv[++i]);
atari_ntsc_setup->burst_phase = atof(argv[++i]); else a_m = TRUE;
}else if (strcmp(argv[i], "-ntsc_gauss") == 0){ }
atari_ntsc_setup->gaussian_factor = atof(argv[++i]); else if (strcmp(argv[i], "-ntsc-artifacts") == 0) {
}else if (strcmp(argv[i], "-ntsc_gamma") == 0){ if (i_a)
atari_ntsc_setup->gamma_adj = atof(argv[++i]); mySetup.artifacts = atof(argv[++i]);
}else if (strcmp(argv[i], "-ntsc_ramp") == 0){ else a_m = TRUE;
atari_ntsc_setup->saturation_ramp = atof(argv[++i]); }
else if (strcmp(argv[i], "-ntsc-fringing") == 0) {
if (i_a)
mySetup.fringing = atof(argv[++i]);
else a_m = TRUE;
}
else if (strcmp(argv[i], "-ntsc-bleed") == 0) {
if (i_a)
mySetup.bleed = atof(argv[++i]);
else a_m = TRUE;
}
else if (strcmp(argv[i], "-ntsc-burstphase") == 0) {
if (i_a)
mySetup.burst_phase = atof(argv[++i]);
else a_m = TRUE;
}
else if (strcmp(argv[i], "-ntsc-filter-preset") == 0) {
if (i_a) {
int idx = CFG_MatchTextParameter(argv[++i], preset_cfg_strings, FILTER_NTSC_PRESET_SIZE);
if (idx < 0) {
Log_print("Invalid value for -ntsc-filter-preset");
return FALSE;
}
setPreset(idx);
} else a_m = TRUE;
} }
else { else {
if (strcmp(argv[i], "-help") == 0) { if (strcmp(argv[i], "-help") == 0) {
Log_print("\t-ntsc_hue <n> Set NTSC hue -1..1 (default %.2g) (-ntsc_emu only)",atari_ntsc_setup->hue); Log_print("\t-ntsc-sharpness <n> Set sharpness for NTSC filter (default %.2g)", mySetup.sharpness);
Log_print("\t-ntsc_sat <n> Set NTSC saturation (default %.2g) (-ntsc_emu only)",atari_ntsc_setup->saturation); Log_print("\t-ntsc-resolution <n> Set resolution for NTSC filter (default %.2g)", mySetup.resolution);
Log_print("\t-ntsc_cont <n> Set NTSC contrast (default %.2g) (-ntsc_emu only)",atari_ntsc_setup->contrast); Log_print("\t-ntsc-artifacts <n> Set luma artifacts ratio for NTSC filter (default %.2g)", mySetup.artifacts);
Log_print("\t-ntsc_bright <n> Set NTSC brightness (default %.2g) (-ntsc_emu only)",atari_ntsc_setup->brightness); Log_print("\t-ntsc-fringing <n> Set chroma fringing ratio for NTSC filter (default %.2g)", mySetup.fringing);
Log_print("\t-ntsc_sharp <n> Set NTSC sharpness (default %.2g) (-ntsc_emu only)",atari_ntsc_setup->sharpness); Log_print("\t-ntsc-bleed <n> Set bleed for NTSC filter (default %.2g)", mySetup.bleed);
Log_print("\t-ntsc_burst <n> Set NTSC burst phase -1..1 (artif colours)(def: %.2g)",atari_ntsc_setup->burst_phase); Log_print("\t-ntsc-burstphase <n> Set burst phase (artifact colours) for NTSC filter (default %.2g)", mySetup.burst_phase);
Log_print("\t (-ntsc_emu only)"); Log_print("\t-ntsc-filter-preset composite|svideo|rgb|monochrome");
Log_print("\t-ntsc_gauss <n> Set NTSC Gaussian factor (default %.2g)",atari_ntsc_setup->gaussian_factor); Log_print("\t Use one of predefined NTSC filter adjustments");
Log_print("\t (-ntsc_emu only)");
Log_print("\t-ntsc_gamma <n> Set NTSC gamma adjustment (default %.2g)",atari_ntsc_setup->gamma_adj);
Log_print("\t (-ntsc_emu only)");
Log_print("\t-ntsc_ramp <n> Set NTSC saturation ramp factor (default %.2g)",atari_ntsc_setup->saturation_ramp);
Log_print("\t (-ntsc_emu only)");
} }
argv[j++] = argv[i]; argv[j++] = argv[i];
} }
if (a_m) {
Log_print("Missing argument for '%s'", argv[i]);
return FALSE;
}
} }
*argc = j; *argc = j;
return TRUE;
} }
#endif #endif
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void NTSCFilter::updateYIQTable(double yiq_table[768], double start_angle)
{
const double start_saturation = 0.0; // calculated internally
const double gamma = 1; // 1 - COLOURS_NTSC_setup.gamma / 2.0;
unsigned char *ext_ptr = NULL; //FIXME COLOURS_NTSC_external.palette;
int n;
start_angle = - ((213.0f) * M_PI / 180.0f) - start_angle;
for (n = 0; n < 256; n ++) {
/* Convert RGB values from external palette to YIQ. */
double r = (double)*ext_ptr++ / 255.0;
double g = (double)*ext_ptr++ / 255.0;
double b = (double)*ext_ptr++ / 255.0;
double y = 0.299 * r + 0.587 * g + 0.114 * b;
double i = 0.595716 * r - 0.274453 * g - 0.321263 * b;
double q = 0.211456 * r - 0.522591 * g + 0.311135 * b;
double s = sin(start_angle);
double c = cos(start_angle);
double tmp_i = i;
i = tmp_i * c - q * s;
q = tmp_i * s + q * c;
#if 0
/* Optionally adjust external palette. */
if (COLOURS_NTSC_external.adjust) {
y = pow(y, gamma);
y *= COLOURS_NTSC_setup.contrast * 0.5 + 1;
y += COLOURS_NTSC_setup.brightness * 0.5;
if (y > 1.0)
y = 1.0;
else if (y < 0.0)
y = 0.0;
i *= start_saturation + 1;
q *= start_saturation + 1;
}
#endif
*yiq_table++ = y;
*yiq_table++ = i;
*yiq_table++ = q;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
atari_ntsc_setup_t const * const NTSCFilter::presets[NTSCFilter::PRESET_SIZE] = {
&atari_ntsc_composite,
&atari_ntsc_svideo,
&atari_ntsc_rgb,
&atari_ntsc_monochrome
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static char const * const preset_cfg_strings[NTSCFilter::PRESET_SIZE] = {
"COMPOSITE",
"SVIDEO",
"RGB",
"MONOCHROME"
};
#endif // DISPLAY_TV #endif // DISPLAY_TV

View File

@ -24,21 +24,27 @@
#include "bspf.hxx" #include "bspf.hxx"
#include "Array.hxx" #include "Array.hxx"
#include "atari_ntsc.h"
// Although the blitter supports 15 and 16 bit, we always use 16-bit // Limits for the adjustable values.
#ifndef ATARI_NTSC_RGB_BITS #define FILTER_NTSC_SHARPNESS_MIN -1.0
#define ATARI_NTSC_RGB_BITS 16 #define FILTER_NTSC_SHARPNESS_MAX 1.0
#endif #define FILTER_NTSC_RESOLUTION_MIN -1.0
#define FILTER_NTSC_RESOLUTION_MAX 1.0
#define FILTER_NTSC_ARTIFACTS_MIN -1.0
#define FILTER_NTSC_ARTIFACTS_MAX 1.0
#define FILTER_NTSC_FRINGING_MIN -1.0
#define FILTER_NTSC_FRINGING_MAX 1.0
#define FILTER_NTSC_BLEED_MIN -1.0
#define FILTER_NTSC_BLEED_MAX 1.0
#define FILTER_NTSC_BURST_PHASE_MIN -1.0
#define FILTER_NTSC_BURST_PHASE_MAX 1.0
/** /**
This class is based on the Blargg NTSC filter code from Atari800MacX. This class is based on the Blargg NTSC filter code from Atari800,
and is derived from 'filter_ntsc.(h|c)'.
Original code based on implementation from http://www.slack.net/~ant Original code based on implementation from http://www.slack.net/~ant.
Based on algorithm by NewRisingSun
License note by Perry: Expat License.
http://www.gnu.org/licenses/license-list.html#GPLCompatibleLicenses
"This is a simple, permissive non-copyleft free software license, compatible with the GNU GPL."
Atari TIA NTSC composite video to RGB emulator/blitter. Atari TIA NTSC composite video to RGB emulator/blitter.
*/ */
@ -48,112 +54,61 @@ class NTSCFilter
NTSCFilter(); NTSCFilter();
virtual ~NTSCFilter(); virtual ~NTSCFilter();
/* Set/get one of the available preset adjustments: Composite, S-Video, RGB,
Monochrome. */
enum {
PRESET_COMPOSITE,
PRESET_SVIDEO,
PRESET_RGB,
PRESET_MONOCHROME,
PRESET_CUSTOM,
/* Number of "normal" (not including CUSTOM) values in enumerator */
PRESET_SIZE = PRESET_CUSTOM
};
public: public:
/**
Cycle through each available NTSC preset mode
@return A message explaining the current NTSC preset mode /* Restores default values for NTSC-filter-specific colour controls.
updateFilter should be called afterwards to apply changes. */
void restoreDefaults();
/* updateFilter should be called afterwards these functions to apply changes. */
void setPreset(int preset);
int getPreset();
void nextPreset();
#if 0 // FIXME
/* Read/write to configuration file. */
int FILTER_NTSC_ReadConfig(char *option, char *ptr);
void FILTER_NTSC_WriteConfig(FILE *fp);
/* NTSC filter initialisation and processing of command-line arguments. */
int FILTER_NTSC_Initialise(int *argc, char *argv[]);
#endif
private:
/* Reinitialises the an NTSC filter. Should be called after changing
palette setup or loading/unloading an external palette. */
void updateFilter();
// The following function is originally from colours_ntsc.
/* Creates YIQ_TABLE from external palette. START_ANGLE and START_SATURATIION
are provided as parameters, because NTSC_FILTER needs to set these values
according to its internal setup (burst_phase etc).
*/ */
const string& next(); static void updateYIQTable(double yiq_table[768], double start_angle);
/* Blit one or more scanlines of Atari 8-bit palette values to 16-bit 5-6-5 RGB output.
For every 7 output pixels, reads approximately 4 source pixels. Use constants below for
definite input and output pixel counts. */
void atari_ntsc_blit( unsigned char const* atari_in, long in_pitch,
int out_width, int out_height, unsigned short* rgb_out, long out_pitch );
// Atari800 Initialise function by perrym
// void ATARI_NTSC_DEFAULTS_Initialise(int *argc, char *argv[], atari_ntsc_setup_t *atari_ntsc_setup);
private: private:
/* Picture parameters, ranging from -1.0 to 1.0 where 0.0 is normal. To easily // Pointer to the NTSC filter structure
clear all fields, make it a static object then set whatever fields you want: atari_ntsc_t myFilter;
static snes_ntsc_setup_t setup;
setup.hue = ...
*/
struct atari_ntsc_setup_t
{
float hue;
float saturation;
float contrast;
float brightness;
float sharpness;
float burst_phase; // not in radians; -1.0 = -180 degrees, 1.0 = +180 degrees
float gaussian_factor;
float gamma_adj; // gamma adjustment
float saturation_ramp; // lower saturation for higher luma values
};
enum { // Contains controls used to adjust the palette in the NTSC filter
atari_ntsc_entry_size = 56, atari_ntsc_setup_t mySetup;
atari_ntsc_color_count = 256,
// Useful values to use for output width and number of input pixels read
atari_ntsc_min_out_width = 570, // minimum width that doesn't cut off active area
atari_ntsc_min_in_width = 320,
atari_ntsc_full_out_width = 598, // room for 8-pixel left & right overscan borders
atari_ntsc_full_in_width = 336,
// Originally present in .c file
composite_border = 6,
center_offset = 1,
alignment_count = 4, // different pixel alignments with respect to yiq quads */
rgb_kernel_size = atari_ntsc_entry_size / alignment_count,
// Originally present in .c file
composite_size = composite_border + 8 + composite_border,
rgb_pad = (center_offset + composite_border + 7) / 8 * 8 - center_offset - composite_border,
rgb_size = (rgb_pad + composite_size + 7) / 8 * 8,
rescaled_size = rgb_size / 8 * 7,
ntsc_kernel_size = composite_size * 2
};
typedef uInt32 ntsc_rgb_t;
// Caller must allocate space for blitter data, which uses 56 KB of memory.
struct atari_ntsc_t
{
ntsc_rgb_t table [atari_ntsc_color_count] [atari_ntsc_entry_size];
};
// Originall present in .c file
struct ntsc_to_rgb_t
{
float composite [composite_size];
float to_rgb [6];
float brightness;
float contrast;
float sharpness;
short rgb [rgb_size] [3];
short rescaled [rescaled_size + 1] [3]; /* extra space for sharpen */
float kernel [ntsc_kernel_size];
};
atari_ntsc_t* myNTSCEmu;
int myCurrentModeNum; int myCurrentModeNum;
Common::Array<atari_ntsc_setup_t> myModeList; Common::Array<atari_ntsc_setup_t> myModeList;
private: static atari_ntsc_setup_t const * const presets[PRESET_SIZE];
/* Initialize and adjust parameters. Can be called multiple times on the same static char const * const preset_cfg_strings[PRESET_SIZE];
atari_ntsc_t object. */
void atari_ntsc_init( atari_ntsc_setup_t const* setup );
static void rotate_matrix( float const* in, float s, float c, float* out );
static void ntsc_to_rgb_init( ntsc_to_rgb_t* ntsc, atari_ntsc_setup_t const* setup, float hue );
/* Convert NTSC composite signal to RGB, where composite signal contains
only four non-zero samples beginning at offset */
static void ntsc_to_rgb( ntsc_to_rgb_t const* ntsc, int offset, short* out );
/* Rescale pixels to NTSC aspect ratio using linear interpolation,
with 7 output pixels for every 8 input pixels, linear interpolation */
static void rescale( short const* in, int count, short* out );
/* Sharpen image using (level-1)/2, level, (level-1)/2 convolution kernel */
static void sharpen( short const* in, float level, int count, short* out );
/* Generate pixel and capture into table */
static ntsc_rgb_t* gen_pixel( ntsc_to_rgb_t* ntsc, int ntsc_pos, int rescaled_pos, ntsc_rgb_t* out );
}; };
#endif // DISPLAY_TV #endif // DISPLAY_TV

View File

@ -0,0 +1,397 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2012 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id$
//============================================================================
/* Based on nes_ntsc 0.2.2. http://www.slack.net/~ant/ */
#ifdef DISPLAY_TV
#include "atari_ntsc.h"
/* Copyright (C) 2006-2007 Shay Green. This module is free software; you
can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version. This
module is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
details. You should have received a copy of the GNU Lesser General Public
License along with this module; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
/* Atari change: removal and addition of structure fields.
Values of resolution and sharpness adjusted to make NTSC artifacts look better. */
atari_ntsc_setup_t const atari_ntsc_monochrome = { 0, -1, 0, 0, -.3, .3, .2, -.2, -.2, -1, 0, 0, 0, 0. };
atari_ntsc_setup_t const atari_ntsc_composite = { 0, 0, 0, 0, -.5, .3, -.1, 0, 0, 0, 0, 0, 0, 0. };
atari_ntsc_setup_t const atari_ntsc_svideo = { 0, 0, 0, 0, -.3, .3, .2, -1, -1, 0, 0, 0, 0, 0. };
atari_ntsc_setup_t const atari_ntsc_rgb = { 0, 0, 0, 0, -.3, .3, .7, -1, -1, -1, 0, 0, 0, 0. };
#define alignment_count 4
#define burst_count 1
#define rescale_in 8
#define rescale_out 7
#define artifacts_mid 1.0f
#define fringing_mid 1.0f
/* Atari change: default palette is already at correct hue.
#define std_decoder_hue -15 */
#define std_decoder_hue 0
/* Atari change: only one palette - remove base_palete field. */
#define STD_HUE_CONDITION( setup ) !(setup->palette)
#include "atari_ntsc_impl.h"
/* Atari change: adapted to 4/7 pixel ratio. */
/* 4 input pixels -> 8 composite samples */
pixel_info_t const atari_ntsc_pixels [alignment_count] = {
{ PIXEL_OFFSET( -6, -6 ), { 0, 0, 1, 1 } },
{ PIXEL_OFFSET( -4, -4 ), { 0, 0, 1, 1 } },
{ PIXEL_OFFSET( -2, -2 ), { 0, 0, 1, 1 } },
{ PIXEL_OFFSET( 0, 0 ), { 0, 0, 1, 1 } },
};
/* Atari change: no alternating burst phases - removed merge_kernel_fields function. */
static void correct_errors( atari_ntsc_rgb_t color, atari_ntsc_rgb_t* out )
{
int n;
for ( n = burst_count; n; --n )
{
unsigned i;
for ( i = 0; i < rgb_kernel_size / 2; i++ )
{
/* Atari change: adapted to 4/7 pixel ratio */
atari_ntsc_rgb_t error = color -
out [i ] - out [(i+12)%14+14] - out [(i+10)%14+28] - out[(i+8)%14+42] -
out [i + 7] - out [i + 5 +14] - out [i + 3 +28] - out [ i+1 +42];
DISTRIBUTE_ERROR( i+1+42, i+3+28, i+5+14, i+7 );
}
out += alignment_count * rgb_kernel_size;
}
}
void atari_ntsc_init( atari_ntsc_t* ntsc, atari_ntsc_setup_t const* setup )
{
/* Atari change: no alternating burst phases - remove merge_fields variable. */
int entry;
init_t impl;
/* Atari change: NES palette generation and reading removed.
Atari palette generation is located in colours_ntsc.c, and colours are read
from setup->yiq_palette. */
if ( !setup )
setup = &atari_ntsc_composite;
init( &impl, setup );
/* Atari change: no alternating burst phases - remove code for merge_fields. */
for ( entry = 0; entry < atari_ntsc_palette_size; entry++ )
{
/* Atari change: Instead of palette generation, load colours
from setup->yiq_palette. */
double y;
double i;
double q;
{
double *yiq_ptr = setup->yiq_palette + 3 * entry;
y = *yiq_ptr++;
i = *yiq_ptr++;
q = *yiq_ptr++;
}
i *= rgb_unit;
q *= rgb_unit;
y *= rgb_unit;
y += rgb_offset;
/* Generate kernel */
{
int r, g, b = YIQ_TO_RGB( y, i, q, impl.to_rgb, int, r, g );
/* blue tends to overflow, so clamp it */
atari_ntsc_rgb_t rgb = PACK_RGB( r, g, (b < 0x3E0 ? b: 0x3E0) );
if ( setup->palette_out )
RGB_PALETTE_OUT( rgb, &setup->palette_out [entry * 3] );
if ( ntsc )
{
atari_ntsc_rgb_t* kernel = ntsc->table [entry];
gen_kernel( &impl, y, i, q, kernel );
/* Atari change: no alternating burst phases - remove code for merge_fields. */
correct_errors( rgb, kernel );
}
}
}
}
#ifndef ATARI_NTSC_NO_BLITTERS
/* Atari change: no alternating burst phases - remove burst_phase parameter.
Also removed the atari_ntsc_blit function and added specific blitters for various
pixel formats. */
#include <limits.h>
#if USHRT_MAX == 0xFFFF
typedef unsigned short atari_ntsc_out16_t;
#else
#error "Need 16-bit int type"
#endif
#if UINT_MAX == 0xFFFFFFFF
typedef unsigned int atari_ntsc_out32_t;
#elif ULONG_MAX == 0xFFFFFFFF
typedef unsigned long atari_ntsc_out32_t;
#else
#error "Need 32-bit int type"
#endif
void atari_ntsc_blit_rgb16( atari_ntsc_t const* ntsc, ATARI_NTSC_IN_T const* input, long in_row_width,
int in_width, int in_height, void* rgb_out, long out_pitch )
{
int chunk_count = (in_width - 1) / atari_ntsc_in_chunk;
for ( ; in_height; --in_height )
{
ATARI_NTSC_IN_T const* line_in = input;
/* Atari change: no alternating burst phases - remove burst_phase parameter; adjust to 4/7 pixel ratio. */
ATARI_NTSC_BEGIN_ROW( ntsc,
atari_ntsc_black, atari_ntsc_black, atari_ntsc_black, ATARI_NTSC_ADJ_IN( *line_in ) );
atari_ntsc_out16_t* restrict line_out = (atari_ntsc_out16_t*) rgb_out;
int n;
++line_in;
for ( n = chunk_count; n; --n )
{
/* order of input and output pixels must not be altered */
ATARI_NTSC_COLOR_IN( 0, ATARI_NTSC_ADJ_IN( line_in [0] ) );
ATARI_NTSC_RGB_OUT( 0, line_out [0], ATARI_NTSC_RGB_FORMAT_RGB16 );
ATARI_NTSC_RGB_OUT( 1, line_out [1], ATARI_NTSC_RGB_FORMAT_RGB16 );
ATARI_NTSC_COLOR_IN( 1, ATARI_NTSC_ADJ_IN( line_in [1] ) );
ATARI_NTSC_RGB_OUT( 2, line_out [2], ATARI_NTSC_RGB_FORMAT_RGB16 );
ATARI_NTSC_RGB_OUT( 3, line_out [3], ATARI_NTSC_RGB_FORMAT_RGB16 );
ATARI_NTSC_COLOR_IN( 2, ATARI_NTSC_ADJ_IN( line_in [2] ) );
ATARI_NTSC_RGB_OUT( 4, line_out [4], ATARI_NTSC_RGB_FORMAT_RGB16 );
ATARI_NTSC_RGB_OUT( 5, line_out [5], ATARI_NTSC_RGB_FORMAT_RGB16 );
ATARI_NTSC_COLOR_IN( 3, ATARI_NTSC_ADJ_IN( line_in [3] ) );
ATARI_NTSC_RGB_OUT( 6, line_out [6], ATARI_NTSC_RGB_FORMAT_RGB16 );
line_in += 4;
line_out += 7;
}
/* finish final pixels */
ATARI_NTSC_COLOR_IN( 0, atari_ntsc_black );
ATARI_NTSC_RGB_OUT( 0, line_out [0], ATARI_NTSC_RGB_FORMAT_RGB16 );
ATARI_NTSC_RGB_OUT( 1, line_out [1], ATARI_NTSC_RGB_FORMAT_RGB16 );
ATARI_NTSC_COLOR_IN( 1, atari_ntsc_black );
ATARI_NTSC_RGB_OUT( 2, line_out [2], ATARI_NTSC_RGB_FORMAT_RGB16 );
ATARI_NTSC_RGB_OUT( 3, line_out [3], ATARI_NTSC_RGB_FORMAT_RGB16 );
ATARI_NTSC_COLOR_IN( 2, atari_ntsc_black );
ATARI_NTSC_RGB_OUT( 4, line_out [4], ATARI_NTSC_RGB_FORMAT_RGB16 );
ATARI_NTSC_RGB_OUT( 5, line_out [5], ATARI_NTSC_RGB_FORMAT_RGB16 );
ATARI_NTSC_COLOR_IN( 3, atari_ntsc_black );
ATARI_NTSC_RGB_OUT( 6, line_out [6], ATARI_NTSC_RGB_FORMAT_RGB16 );
input += in_row_width;
rgb_out = (char*) rgb_out + out_pitch;
}
}
void atari_ntsc_blit_bgr16( atari_ntsc_t const* ntsc, ATARI_NTSC_IN_T const* input, long in_row_width,
int in_width, int in_height, void* rgb_out, long out_pitch )
{
int chunk_count = (in_width - 1) / atari_ntsc_in_chunk;
for ( ; in_height; --in_height )
{
ATARI_NTSC_IN_T const* line_in = input;
/* Atari change: no alternating burst phases - remove burst_phase parameter; adjust to 4/7 pixel ratio. */
ATARI_NTSC_BEGIN_ROW( ntsc,
atari_ntsc_black, atari_ntsc_black, atari_ntsc_black, ATARI_NTSC_ADJ_IN( *line_in ) );
atari_ntsc_out16_t* restrict line_out = (atari_ntsc_out16_t*) rgb_out;
int n;
++line_in;
for ( n = chunk_count; n; --n )
{
/* order of input and output pixels must not be altered */
ATARI_NTSC_COLOR_IN( 0, ATARI_NTSC_ADJ_IN( line_in [0] ) );
ATARI_NTSC_RGB_OUT( 0, line_out [0], ATARI_NTSC_RGB_FORMAT_BGR16 );
ATARI_NTSC_RGB_OUT( 1, line_out [1], ATARI_NTSC_RGB_FORMAT_BGR16 );
ATARI_NTSC_COLOR_IN( 1, ATARI_NTSC_ADJ_IN( line_in [1] ) );
ATARI_NTSC_RGB_OUT( 2, line_out [2], ATARI_NTSC_RGB_FORMAT_BGR16 );
ATARI_NTSC_RGB_OUT( 3, line_out [3], ATARI_NTSC_RGB_FORMAT_BGR16 );
ATARI_NTSC_COLOR_IN( 2, ATARI_NTSC_ADJ_IN( line_in [2] ) );
ATARI_NTSC_RGB_OUT( 4, line_out [4], ATARI_NTSC_RGB_FORMAT_BGR16 );
ATARI_NTSC_RGB_OUT( 5, line_out [5], ATARI_NTSC_RGB_FORMAT_BGR16 );
ATARI_NTSC_COLOR_IN( 3, ATARI_NTSC_ADJ_IN( line_in [3] ) );
ATARI_NTSC_RGB_OUT( 6, line_out [6], ATARI_NTSC_RGB_FORMAT_BGR16 );
line_in += 4;
line_out += 7;
}
/* finish final pixels */
ATARI_NTSC_COLOR_IN( 0, atari_ntsc_black );
ATARI_NTSC_RGB_OUT( 0, line_out [0], ATARI_NTSC_RGB_FORMAT_BGR16 );
ATARI_NTSC_RGB_OUT( 1, line_out [1], ATARI_NTSC_RGB_FORMAT_BGR16 );
ATARI_NTSC_COLOR_IN( 1, atari_ntsc_black );
ATARI_NTSC_RGB_OUT( 2, line_out [2], ATARI_NTSC_RGB_FORMAT_BGR16 );
ATARI_NTSC_RGB_OUT( 3, line_out [3], ATARI_NTSC_RGB_FORMAT_BGR16 );
ATARI_NTSC_COLOR_IN( 2, atari_ntsc_black );
ATARI_NTSC_RGB_OUT( 4, line_out [4], ATARI_NTSC_RGB_FORMAT_BGR16 );
ATARI_NTSC_RGB_OUT( 5, line_out [5], ATARI_NTSC_RGB_FORMAT_BGR16 );
ATARI_NTSC_COLOR_IN( 3, atari_ntsc_black );
ATARI_NTSC_RGB_OUT( 6, line_out [6], ATARI_NTSC_RGB_FORMAT_BGR16 );
input += in_row_width;
rgb_out = (char*) rgb_out + out_pitch;
}
}
void atari_ntsc_blit_argb32( atari_ntsc_t const* ntsc, ATARI_NTSC_IN_T const* input, long in_row_width,
int in_width, int in_height, void* rgb_out, long out_pitch )
{
int chunk_count = (in_width - 1) / atari_ntsc_in_chunk;
for ( ; in_height; --in_height )
{
ATARI_NTSC_IN_T const* line_in = input;
/* Atari change: no alternating burst phases - remove burst_phase parameter; adjust to 4/7 pixel ratio. */
ATARI_NTSC_BEGIN_ROW( ntsc,
atari_ntsc_black, atari_ntsc_black, atari_ntsc_black, ATARI_NTSC_ADJ_IN( *line_in ) );
atari_ntsc_out32_t* restrict line_out = (atari_ntsc_out32_t*) rgb_out;
int n;
++line_in;
for ( n = chunk_count; n; --n )
{
/* order of input and output pixels must not be altered */
ATARI_NTSC_COLOR_IN( 0, ATARI_NTSC_ADJ_IN( line_in [0] ) );
ATARI_NTSC_RGB_OUT( 0, line_out [0], ATARI_NTSC_RGB_FORMAT_ARGB32 );
ATARI_NTSC_RGB_OUT( 1, line_out [1], ATARI_NTSC_RGB_FORMAT_ARGB32 );
ATARI_NTSC_COLOR_IN( 1, ATARI_NTSC_ADJ_IN( line_in [1] ) );
ATARI_NTSC_RGB_OUT( 2, line_out [2], ATARI_NTSC_RGB_FORMAT_ARGB32 );
ATARI_NTSC_RGB_OUT( 3, line_out [3], ATARI_NTSC_RGB_FORMAT_ARGB32 );
ATARI_NTSC_COLOR_IN( 2, ATARI_NTSC_ADJ_IN( line_in [2] ) );
ATARI_NTSC_RGB_OUT( 4, line_out [4], ATARI_NTSC_RGB_FORMAT_ARGB32 );
ATARI_NTSC_RGB_OUT( 5, line_out [5], ATARI_NTSC_RGB_FORMAT_ARGB32 );
ATARI_NTSC_COLOR_IN( 3, ATARI_NTSC_ADJ_IN( line_in [3] ) );
ATARI_NTSC_RGB_OUT( 6, line_out [6], ATARI_NTSC_RGB_FORMAT_ARGB32 );
line_in += 4;
line_out += 7;
}
/* finish final pixels */
ATARI_NTSC_COLOR_IN( 0, atari_ntsc_black );
ATARI_NTSC_RGB_OUT( 0, line_out [0], ATARI_NTSC_RGB_FORMAT_ARGB32 );
ATARI_NTSC_RGB_OUT( 1, line_out [1], ATARI_NTSC_RGB_FORMAT_ARGB32 );
ATARI_NTSC_COLOR_IN( 1, atari_ntsc_black );
ATARI_NTSC_RGB_OUT( 2, line_out [2], ATARI_NTSC_RGB_FORMAT_ARGB32 );
ATARI_NTSC_RGB_OUT( 3, line_out [3], ATARI_NTSC_RGB_FORMAT_ARGB32 );
ATARI_NTSC_COLOR_IN( 2, atari_ntsc_black );
ATARI_NTSC_RGB_OUT( 4, line_out [4], ATARI_NTSC_RGB_FORMAT_ARGB32 );
ATARI_NTSC_RGB_OUT( 5, line_out [5], ATARI_NTSC_RGB_FORMAT_ARGB32 );
ATARI_NTSC_COLOR_IN( 3, atari_ntsc_black );
ATARI_NTSC_RGB_OUT( 6, line_out [6], ATARI_NTSC_RGB_FORMAT_ARGB32 );
input += in_row_width;
rgb_out = (char*) rgb_out + out_pitch;
}
}
void atari_ntsc_blit_bgra32( atari_ntsc_t const* ntsc, ATARI_NTSC_IN_T const* input, long in_row_width,
int in_width, int in_height, void* rgb_out, long out_pitch )
{
int chunk_count = (in_width - 1) / atari_ntsc_in_chunk;
for ( ; in_height; --in_height )
{
ATARI_NTSC_IN_T const* line_in = input;
/* Atari change: no alternating burst phases - remove burst_phase parameter; adjust to 4/7 pixel ratio. */
ATARI_NTSC_BEGIN_ROW( ntsc,
atari_ntsc_black, atari_ntsc_black, atari_ntsc_black, ATARI_NTSC_ADJ_IN( *line_in ) );
atari_ntsc_out32_t* restrict line_out = (atari_ntsc_out32_t*) rgb_out;
int n;
++line_in;
for ( n = chunk_count; n; --n )
{
/* order of input and output pixels must not be altered */
ATARI_NTSC_COLOR_IN( 0, ATARI_NTSC_ADJ_IN( line_in [0] ) );
ATARI_NTSC_RGB_OUT( 0, line_out [0], ATARI_NTSC_RGB_FORMAT_BGRA32 );
ATARI_NTSC_RGB_OUT( 1, line_out [1], ATARI_NTSC_RGB_FORMAT_BGRA32 );
ATARI_NTSC_COLOR_IN( 1, ATARI_NTSC_ADJ_IN( line_in [1] ) );
ATARI_NTSC_RGB_OUT( 2, line_out [2], ATARI_NTSC_RGB_FORMAT_BGRA32 );
ATARI_NTSC_RGB_OUT( 3, line_out [3], ATARI_NTSC_RGB_FORMAT_BGRA32 );
ATARI_NTSC_COLOR_IN( 2, ATARI_NTSC_ADJ_IN( line_in [2] ) );
ATARI_NTSC_RGB_OUT( 4, line_out [4], ATARI_NTSC_RGB_FORMAT_BGRA32 );
ATARI_NTSC_RGB_OUT( 5, line_out [5], ATARI_NTSC_RGB_FORMAT_BGRA32 );
ATARI_NTSC_COLOR_IN( 3, ATARI_NTSC_ADJ_IN( line_in [3] ) );
ATARI_NTSC_RGB_OUT( 6, line_out [6], ATARI_NTSC_RGB_FORMAT_BGRA32 );
line_in += 4;
line_out += 7;
}
/* finish final pixels */
ATARI_NTSC_COLOR_IN( 0, atari_ntsc_black );
ATARI_NTSC_RGB_OUT( 0, line_out [0], ATARI_NTSC_RGB_FORMAT_BGRA32 );
ATARI_NTSC_RGB_OUT( 1, line_out [1], ATARI_NTSC_RGB_FORMAT_BGRA32 );
ATARI_NTSC_COLOR_IN( 1, atari_ntsc_black );
ATARI_NTSC_RGB_OUT( 2, line_out [2], ATARI_NTSC_RGB_FORMAT_BGRA32 );
ATARI_NTSC_RGB_OUT( 3, line_out [3], ATARI_NTSC_RGB_FORMAT_BGRA32 );
ATARI_NTSC_COLOR_IN( 2, atari_ntsc_black );
ATARI_NTSC_RGB_OUT( 4, line_out [4], ATARI_NTSC_RGB_FORMAT_BGRA32 );
ATARI_NTSC_RGB_OUT( 5, line_out [5], ATARI_NTSC_RGB_FORMAT_BGRA32 );
ATARI_NTSC_COLOR_IN( 3, atari_ntsc_black );
ATARI_NTSC_RGB_OUT( 6, line_out [6], ATARI_NTSC_RGB_FORMAT_BGRA32 );
input += in_row_width;
rgb_out = (char*) rgb_out + out_pitch;
}
}
#endif
#endif // DISPLAY_TV

View File

@ -0,0 +1,268 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2012 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id$
//============================================================================
#ifdef DISPLAY_TV
/* Atari TIA, CTIA, GTIA and MARIA NTSC video filter */
/* based on nes_ntsc 0.2.2 */
#ifndef ATARI_NTSC_H
#define ATARI_NTSC_H
// The following come from "atari_ntsc_config.h", but since we'll eventually
// be using a custom blitter, most of the items will be redundant
//////////////////////////////////////////////////////////////////////////////
/* The following affect the built-in blitter only; a custom blitter can
handle things however it wants. */
/* Bits per pixel of output. Can be 15, 16, 32, or 24 (same as 32). */
#define ATARI_NTSC_OUT_DEPTH 16
/* Type of input pixel values. You'll probably use unsigned short
if you enable emphasis above. */
#define ATARI_NTSC_IN_T unsigned char
/* Each raw pixel input value is passed through this. You might want to mask
the pixel index if you use the high bits as flags, etc. */
#define ATARI_NTSC_ADJ_IN( in ) in
//////////////////////////////////////////////////////////////////////////////
#ifdef __cplusplus
extern "C" {
#endif
/* Image parameters, ranging from -1.0 to 1.0. Actual internal values shown
in parenthesis and should remain fairly stable in future versions. */
typedef struct atari_ntsc_setup_t
{
/* Basic parameters */
double hue; /* -1 = -180 degrees +1 = +180 degrees */
double saturation; /* -1 = grayscale (0.0) +1 = oversaturated colors (2.0) */
double contrast; /* -1 = dark (0.5) +1 = light (1.5) */
double brightness; /* -1 = dark (0.5) +1 = light (1.5) */
double sharpness; /* edge contrast enhancement/blurring */
/* Advanced parameters */
double gamma; /* -1 = dark (1.5) +1 = light (0.5) */
double resolution; /* image resolution */
double artifacts; /* artifacts caused by color changes */
double fringing; /* color artifacts caused by brightness changes */
double bleed; /* color bleed (color resolution reduction) */
/* Atari change: no alternating burst phases - remove merge_fields field. */
float const* decoder_matrix; /* optional RGB decoder matrix, 6 elements */
unsigned char* palette_out; /* optional RGB palette out, 3 bytes per color */
/* You can replace the standard NES color generation with an RGB palette. The
first replaces all color generation, while the second replaces only the core
64-color generation and does standard color emphasis calculations on it. */
unsigned char const* palette;/* optional 256-entry RGB palette in, 3 bytes per color */
/* Atari change: only one palette - remove base_palette field. */
/* Atari change: additional setup fields */
double burst_phase; /* Phase at which colorburst signal is turned on;
this defines colors of artifacts.
In radians; -1.0 = -180 degrees, 1.0 = +180 degrees */
double *yiq_palette;
} atari_ntsc_setup_t;
/* Video format presets */
extern atari_ntsc_setup_t const atari_ntsc_composite; /* color bleeding + artifacts */
extern atari_ntsc_setup_t const atari_ntsc_svideo; /* color bleeding only */
extern atari_ntsc_setup_t const atari_ntsc_rgb; /* crisp image */
extern atari_ntsc_setup_t const atari_ntsc_monochrome;/* desaturated + artifacts */
enum { atari_ntsc_palette_size = 256 };
/* Initializes and adjusts parameters. Can be called multiple times on the same
atari_ntsc_t object. Can pass NULL for either parameter. */
typedef struct atari_ntsc_t atari_ntsc_t;
void atari_ntsc_init( atari_ntsc_t* ntsc, atari_ntsc_setup_t const* setup );
/* Filters one or more rows of pixels. Input pixels are 6/9-bit palette indicies.
In_row_width is the number of pixels to get to the next input row. Out_pitch
is the number of *bytes* to get to the next output row. Output pixel format
is set by ATARI_NTSC_OUT_DEPTH (defaults to 16-bit RGB). */
/* Atari change: no alternating burst phases - remove burst_phase parameter.
Also removed the atari_ntsc_blit function and added specific blitters for various
pixel formats. */
void atari_ntsc_blit_rgb16( atari_ntsc_t const* ntsc, ATARI_NTSC_IN_T const* atari_in,
long in_row_width, int in_width, int in_height,
void* rgb_out, long out_pitch );
void atari_ntsc_blit_bgr16( atari_ntsc_t const* ntsc, ATARI_NTSC_IN_T const* atari_in,
long in_row_width, int in_width, int in_height,
void* rgb_out, long out_pitch );
void atari_ntsc_blit_argb32( atari_ntsc_t const* ntsc, ATARI_NTSC_IN_T const* atari_in,
long in_row_width, int in_width, int in_height,
void* rgb_out, long out_pitch );
void atari_ntsc_blit_bgra32( atari_ntsc_t const* ntsc, ATARI_NTSC_IN_T const* atari_in,
long in_row_width, int in_width, int in_height,
void* rgb_out, long out_pitch );
/* Number of output pixels written by blitter for given input width. Width might
be rounded down slightly; use ATARI_NTSC_IN_WIDTH() on result to find rounded
value. Guaranteed not to round 256 down at all. */
#define ATARI_NTSC_OUT_WIDTH( in_width ) \
((((in_width) - 1) / atari_ntsc_in_chunk + 1)* atari_ntsc_out_chunk)
/* Number of input pixels that will fit within given output width. Might be
rounded down slightly; use ATARI_NTSC_OUT_WIDTH() on result to find rounded
value. */
#define ATARI_NTSC_IN_WIDTH( out_width ) \
(((out_width) / atari_ntsc_out_chunk - 1) * atari_ntsc_in_chunk + 1)
/* Interface for user-defined custom blitters. */
enum { atari_ntsc_in_chunk = 4 }; /* number of input pixels read per chunk */
enum { atari_ntsc_out_chunk = 7 }; /* number of output pixels generated per chunk */
enum { atari_ntsc_black = 0 }; /* palette index for black */
enum { atari_ntsc_burst_count = 1 }; /* burst phase cycles through 0, 1, and 2 */
/* Begins outputting row and starts three pixels. First pixel will be cut off a bit.
Use nes_ntsc_black for unused pixels. Declares variables, so must be before first
statement in a block (unless you're using C++). */
/* Atari change: no alternating burst phases; adapted to 4/7 pixel ratio. */
#define ATARI_NTSC_BEGIN_ROW( ntsc, pixel0, pixel1, pixel2, pixel3 ) \
char const* const ktable = \
(char const*) (ntsc)->table [0];\
ATARI_NTSC_BEGIN_ROW_8_( pixel0, pixel1, pixel2, pixel3, ATARI_NTSC_ENTRY_, ktable )
/* Begins input pixel */
#define ATARI_NTSC_COLOR_IN( in_index, color_in ) \
ATARI_NTSC_COLOR_IN_( in_index, color_in, ATARI_NTSC_ENTRY_, ktable )
/* Generates output pixel. Bits can be 24, 16, 15, 32 (treated as 24), or 0:
24: RRRRRRRR GGGGGGGG BBBBBBBB (8-8-8 RGB)
16: RRRRRGGG GGGBBBBB (5-6-5 RGB)
15: RRRRRGG GGGBBBBB (5-5-5 RGB)
0: xxxRRRRR RRRxxGGG GGGGGxxB BBBBBBBx (native internal format; x = junk bits) */
#define ATARI_NTSC_RGB_OUT( index, rgb_out, bits ) \
ATARI_NTSC_RGB_OUT_14_( index, rgb_out, bits, 0 )
/* private */
enum { atari_ntsc_entry_size = 56 };
typedef unsigned long atari_ntsc_rgb_t;
struct atari_ntsc_t {
atari_ntsc_rgb_t table [atari_ntsc_palette_size] [atari_ntsc_entry_size];
};
enum { atari_ntsc_burst_size = atari_ntsc_entry_size / atari_ntsc_burst_count };
#define ATARI_NTSC_ENTRY_( ktable, n ) \
(atari_ntsc_rgb_t const*) (ktable + (n) * (atari_ntsc_entry_size * sizeof (atari_ntsc_rgb_t)))
/* deprecated */
#define ATARI_NTSC_RGB24_OUT( x, out ) ATARI_NTSC_RGB_OUT( x, out, 24 )
#define ATARI_NTSC_RGB16_OUT( x, out ) ATARI_NTSC_RGB_OUT( x, out, 16 )
#define ATARI_NTSC_RGB15_OUT( x, out ) ATARI_NTSC_RGB_OUT( x, out, 15 )
#define ATARI_NTSC_RAW_OUT( x, out ) ATARI_NTSC_RGB_OUT( x, out, 0 )
enum { atari_ntsc_min_in_width = 320 }; /* minimum width that doesn't cut off active area */
enum { atari_ntsc_min_out_width = ATARI_NTSC_OUT_WIDTH( atari_ntsc_min_in_width ) };
enum { atari_ntsc_640_in_width = 336 }; /* room for 8-pixel left & right overscan borders */
enum { atari_ntsc_640_out_width = ATARI_NTSC_OUT_WIDTH( atari_ntsc_640_in_width ) };
enum { atari_ntsc_640_overscan_left = 8 };
enum { atari_ntsc_640_overscan_right = atari_ntsc_640_in_width - atari_ntsc_min_in_width - atari_ntsc_640_overscan_left };
enum { atari_ntsc_full_in_width = 384 }; /* room for full overscan */
enum { atari_ntsc_full_out_width = ATARI_NTSC_OUT_WIDTH( atari_ntsc_full_in_width ) };
enum { atari_ntsc_full_overscan_left = 32 };
enum { atari_ntsc_full_overscan_right = atari_ntsc_full_in_width - atari_ntsc_min_in_width - atari_ntsc_full_overscan_left };
/* common 4->7 ntsc macros */
/* Atari change: adapted to 4/7 pixel ratio. */
#define ATARI_NTSC_BEGIN_ROW_8_( pixel0, pixel1, pixel2, pixel3, ENTRY, table ) \
unsigned const atari_ntsc_pixel0_ = (pixel0);\
atari_ntsc_rgb_t const* kernel0 = ENTRY( table, atari_ntsc_pixel0_ );\
unsigned const atari_ntsc_pixel1_ = (pixel1);\
atari_ntsc_rgb_t const* kernel1 = ENTRY( table, atari_ntsc_pixel1_ );\
unsigned const atari_ntsc_pixel2_ = (pixel2);\
atari_ntsc_rgb_t const* kernel2 = ENTRY( table, atari_ntsc_pixel2_ );\
unsigned const atari_ntsc_pixel3_ = (pixel3);\
atari_ntsc_rgb_t const* kernel3 = ENTRY( table, atari_ntsc_pixel3_ );\
atari_ntsc_rgb_t const* kernelx0;\
atari_ntsc_rgb_t const* kernelx1 = kernel0;\
atari_ntsc_rgb_t const* kernelx2 = kernel0;\
atari_ntsc_rgb_t const* kernelx3 = kernel0
/* Atari change: adapted to 4/7 pixel ratio. */
#define ATARI_NTSC_RGB_OUT_14_( x, rgb_out, bits, shift ) {\
atari_ntsc_rgb_t raw_ =\
kernel0 [x ] + kernel1 [(x+5)%7+14] + kernel2 [(x+3)%7+28] + kernel3 [(x+1)%7+42] +\
kernelx0 [(x+7)%14] + kernelx1 [(x+5)%7+21] + kernelx2 [(x+3)%7+35] + kernelx3 [(x+1)%7+49];\
ATARI_NTSC_CLAMP_( raw_, shift );\
ATARI_NTSC_RGB_OUT_( rgb_out, bits, shift );\
}
/* common ntsc macros */
#define atari_ntsc_rgb_builder ((1L << 21) | (1 << 11) | (1 << 1))
#define atari_ntsc_clamp_mask (atari_ntsc_rgb_builder * 3 / 2)
#define atari_ntsc_clamp_add (atari_ntsc_rgb_builder * 0x101)
#define ATARI_NTSC_CLAMP_( io, shift ) {\
atari_ntsc_rgb_t sub = (io) >> (9-(shift)) & atari_ntsc_clamp_mask;\
atari_ntsc_rgb_t clamp = atari_ntsc_clamp_add - sub;\
io |= clamp;\
clamp -= sub;\
io &= clamp;\
}
#define ATARI_NTSC_COLOR_IN_( index, color, ENTRY, table ) {\
unsigned color_;\
kernelx##index = kernel##index;\
kernel##index = (color_ = (color), ENTRY( table, color_ ));\
}
/* Atari change: modified ATARI_NTSC_RGB_OUT_ so its BITS parameter is
no longer a straight number of bits, but an enumerated value. Then
added a few additional bit formats. Also added the ATARI_NTSC_RGB_FORMAT
enumerated values. */
enum {
ATARI_NTSC_RGB_FORMAT_RGB16,
ATARI_NTSC_RGB_FORMAT_BGR16,
ATARI_NTSC_RGB_FORMAT_ARGB32,
ATARI_NTSC_RGB_FORMAT_BGRA32,
ATARI_NTSC_RGB_FORMAT_RGB15
};
/* x is always zero except in snes_ntsc library */
#define ATARI_NTSC_RGB_OUT_( rgb_out, bits, x ) {\
if ( bits == ATARI_NTSC_RGB_FORMAT_RGB16 )\
rgb_out = (raw_>>(13-x)& 0xF800)|(raw_>>(8-x)&0x07E0)|(raw_>>(4-x)&0x001F);\
else if ( bits == ATARI_NTSC_RGB_FORMAT_BGR16 )\
rgb_out = (raw_>>(24-x)& 0x001F)|(raw_>>(8-x)&0x07E0)|(raw_<<(7+x)&0xF800);\
else if ( bits == ATARI_NTSC_RGB_FORMAT_ARGB32 )\
rgb_out = (raw_>>(5-x)&0xFF0000)|(raw_>>(3-x)&0xFF00)|(raw_>>(1-x)&0xFF) | 0xFF000000;\
else if ( bits == ATARI_NTSC_RGB_FORMAT_BGRA32 )\
rgb_out = (raw_>>(13-x)&0xFF00)|(raw_<<(5+x)&0xFF0000)|(raw_<<(23+x)&0xFF000000) | 0xFF;\
else if ( bits == ATARI_NTSC_RGB_FORMAT_RGB15 )\
rgb_out = (raw_>>(14-x)& 0x7C00)|(raw_>>(9-x)&0x03E0)|(raw_>>(4-x)&0x001F);\
else if ( bits == 0 )\
rgb_out = raw_ << x;\
}
#ifdef __cplusplus
}
#endif
#endif
#endif // DISPLAY_TV

View File

@ -0,0 +1,478 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2012 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id$
//============================================================================
#ifdef DISPLAY_TV
/* Based on nes_ntsc 0.2.2. http://www.slack.net/~ant/ */
/* Common implementation of NTSC filters */
#include <assert.h>
#include <math.h>
/* Copyright (C) 2006 Shay Green. This module is free software; you
can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version. This
module is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
details. You should have received a copy of the GNU Lesser General Public
License along with this module; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
#define DISABLE_CORRECTION 0
#undef PI
#define PI 3.14159265358979323846f
#ifndef LUMA_CUTOFF
#define LUMA_CUTOFF 0.20
#endif
#ifndef gamma_size
#define gamma_size 1
#endif
#ifndef rgb_bits
#define rgb_bits 8
#endif
#ifndef artifacts_max
#define artifacts_max (artifacts_mid * 1.5f)
#endif
#ifndef fringing_max
#define fringing_max (fringing_mid * 2)
#endif
#ifndef STD_HUE_CONDITION
#define STD_HUE_CONDITION( setup ) 1
#endif
#define ext_decoder_hue (std_decoder_hue + 15)
#define rgb_unit (1 << rgb_bits)
#define rgb_offset (rgb_unit * 2 + 0.5f)
enum { burst_size = atari_ntsc_entry_size / burst_count };
enum { kernel_half = 16 };
enum { kernel_size = kernel_half * 2 + 1 };
typedef struct init_t
{
float to_rgb [burst_count * 6];
float to_float [gamma_size];
float contrast;
float brightness;
float artifacts;
float fringing;
float kernel [rescale_out * kernel_size * 2];
} init_t;
#define ROTATE_IQ( i, q, sin_b, cos_b ) {\
float t;\
t = i * cos_b - q * sin_b;\
q = i * sin_b + q * cos_b;\
i = t;\
}
static void init_filters( init_t* impl, atari_ntsc_setup_t const* setup )
{
#if rescale_out > 1
float kernels [kernel_size * 2];
#else
float* const kernels = impl->kernel;
#endif
/* generate luma (y) filter using sinc kernel */
{
/* sinc with rolloff (dsf) */
float const rolloff = 1 + (float) setup->sharpness * (float) 0.032;
float const maxh = 32;
float const pow_a_n = (float) pow( rolloff, maxh );
float sum;
int i;
/* quadratic mapping to reduce negative (blurring) range */
float to_angle = (float) setup->resolution + 1;
to_angle = PI / maxh * (float) LUMA_CUTOFF * (to_angle * to_angle + 1);
kernels [kernel_size * 3 / 2] = maxh; /* default center value */
for ( i = 0; i < kernel_half * 2 + 1; i++ )
{
int x = i - kernel_half;
float angle = x * to_angle;
/* instability occurs at center point with rolloff very close to 1.0 */
if ( x || pow_a_n > (float) 1.056 || pow_a_n < (float) 0.981 )
{
float rolloff_cos_a = rolloff * (float) cos( angle );
float num = 1 - rolloff_cos_a -
pow_a_n * (float) cos( maxh * angle ) +
pow_a_n * rolloff * (float) cos( (maxh - 1) * angle );
float den = 1 - rolloff_cos_a - rolloff_cos_a + rolloff * rolloff;
float dsf = num / den;
kernels [kernel_size * 3 / 2 - kernel_half + i] = dsf - (float) 0.5;
}
}
/* apply blackman window and find sum */
sum = 0;
for ( i = 0; i < kernel_half * 2 + 1; i++ )
{
float x = PI * 2 / (kernel_half * 2) * i;
float blackman = 0.42f - 0.5f * (float) cos( x ) + 0.08f * (float) cos( x * 2 );
sum += (kernels [kernel_size * 3 / 2 - kernel_half + i] *= blackman);
}
/* normalize kernel */
sum = 1.0f / sum;
for ( i = 0; i < kernel_half * 2 + 1; i++ )
{
int x = kernel_size * 3 / 2 - kernel_half + i;
kernels [x] *= sum;
assert( kernels [x] == kernels [x] ); /* catch numerical instability */
}
}
/* generate chroma (iq) filter using gaussian kernel */
{
float const cutoff_factor = -0.03125f;
float cutoff = (float) setup->bleed;
int i;
if ( cutoff < 0 )
{
/* keep extreme value accessible only near upper end of scale (1.0) */
cutoff *= cutoff;
cutoff *= cutoff;
cutoff *= cutoff;
cutoff *= -30.0f / 0.65f;
}
cutoff = cutoff_factor - 0.65f * cutoff_factor * cutoff;
for ( i = -kernel_half; i <= kernel_half; i++ )
kernels [kernel_size / 2 + i] = (float) exp( i * i * cutoff );
/* normalize even and odd phases separately */
for ( i = 0; i < 2; i++ )
{
float sum = 0;
int x;
for ( x = i; x < kernel_size; x += 2 )
sum += kernels [x];
sum = 1.0f / sum;
for ( x = i; x < kernel_size; x += 2 )
{
kernels [x] *= sum;
assert( kernels [x] == kernels [x] ); /* catch numerical instability */
}
}
}
/*
printf( "luma:\n" );
for ( i = kernel_size; i < kernel_size * 2; i++ )
printf( "%f\n", kernels [i] );
printf( "chroma:\n" );
for ( i = 0; i < kernel_size; i++ )
printf( "%f\n", kernels [i] );
*/
/* generate linear rescale kernels */
#if rescale_out > 1
{
float weight = 1.0f;
float* out = impl->kernel;
int n = rescale_out;
do
{
float remain = 0;
int i;
weight -= 1.0f / rescale_in;
for ( i = 0; i < kernel_size * 2; i++ )
{
float cur = kernels [i];
float m = cur * weight;
*out++ = m + remain;
remain = cur - m;
}
}
while ( --n );
}
#endif
}
/* Atari change: more accurate values taken from
http://en.wikipedia.org/wiki/YIQ */
static float const default_decoder [6] =
{ 0.9563f, 0.6210f, -0.2721f, -0.6474f, -1.1070f, 1.7046f };
static void init( init_t* impl, atari_ntsc_setup_t const* setup )
{
impl->brightness = (float) setup->brightness * (0.5f * rgb_unit) + rgb_offset;
impl->contrast = (float) setup->contrast * (0.5f * rgb_unit) + rgb_unit;
#ifdef default_palette_contrast
if ( !setup->palette )
impl->contrast *= default_palette_contrast;
#endif
impl->artifacts = (float) setup->artifacts;
if ( impl->artifacts > 0 )
impl->artifacts *= artifacts_max - artifacts_mid;
impl->artifacts = impl->artifacts * artifacts_mid + artifacts_mid;
impl->fringing = (float) setup->fringing;
if ( impl->fringing > 0 )
impl->fringing *= fringing_max - fringing_mid;
impl->fringing = impl->fringing * fringing_mid + fringing_mid;
init_filters( impl, setup );
/* generate gamma table */
if ( gamma_size > 1 )
{
float const to_float = 1.0f / (gamma_size - (gamma_size > 1));
float const gamma = 1.1333f - (float) setup->gamma * 0.5f;
/* match common PC's 2.2 gamma to TV's 2.65 gamma */
int i;
for ( i = 0; i < gamma_size; i++ )
impl->to_float [i] =
(float) pow( i * to_float, gamma ) * impl->contrast + impl->brightness;
}
/* setup decoder matricies */
{
/* Atari change:
NTSC colorburst angle in YIQ colorspace. Colorburst is at
180 degrees in YUV - that is, a gold color. In YIQ, gold is at
different angle. However, YIQ is actually YUV turned
33 degrees. So by looking at screenshots at Wikipedia we can
conclude that the colorburst angle is 180+33 in YIQ.
(See http://en.wikipedia.org/wiki/YUV and
http://en.wikipedia.org/wiki/YIQ) */
static float const colorburst_angle = (213.0f) * PI / 180.0f;
float hue = (float) setup->hue * PI + PI / 180 * ext_decoder_hue - PI * setup->burst_phase - colorburst_angle;
float sat = (float) setup->saturation + 1;
float const* decoder = setup->decoder_matrix;
if ( !decoder )
{
decoder = default_decoder;
if ( STD_HUE_CONDITION( setup ) )
hue += PI / 180 * (std_decoder_hue - ext_decoder_hue);
}
{
float s = (float) sin( hue ) * sat;
float c = (float) cos( hue ) * sat;
float* out = impl->to_rgb;
int n;
n = burst_count;
do
{
float const* in = decoder;
int n = 3;
do
{
float i = *in++;
float q = *in++;
*out++ = i * c - q * s;
*out++ = i * s + q * c;
}
while ( --n );
if ( burst_count <= 1 )
break;
ROTATE_IQ( s, c, 0.866025f, -0.5f ); /* +120 degrees */
}
while ( --n );
}
}
}
/* kernel generation */
/* Atari change: more accurate values taken from
http://en.wikipedia.org/wiki/YIQ */
#define RGB_TO_YIQ( r, g, b, y, i ) (\
(y = (r) * 0.299f + (g) * 0.587f + (b) * 0.114f),\
(i = (r) * 0.595716f - (g) * 0.274453f - (b) * 0.321263f),\
((r) * 0.211456f - (g) * 0.522591f + (b) * 0.311135f)\
)
#define YIQ_TO_RGB( y, i, q, to_rgb, type, r, g ) (\
r = (type) (y + to_rgb [0] * i + to_rgb [1] * q),\
g = (type) (y + to_rgb [2] * i + to_rgb [3] * q),\
(type) (y + to_rgb [4] * i + to_rgb [5] * q)\
)
#define PACK_RGB( r, g, b ) ((r) << 21 | (g) << 11 | (b) << 1)
enum { rgb_kernel_size = burst_size / alignment_count };
enum { rgb_bias = rgb_unit * 2 * atari_ntsc_rgb_builder };
typedef struct pixel_info_t
{
int offset;
float negate;
float kernel [4];
} pixel_info_t;
#if rescale_in > 1
#define PIXEL_OFFSET_( ntsc, scaled ) \
(kernel_size / 2 + ntsc + (scaled != 0) + (rescale_out - scaled) % rescale_out + \
(kernel_size * 2 * scaled))
#define PIXEL_OFFSET( ntsc, scaled ) \
PIXEL_OFFSET_( ((ntsc) - (scaled) / rescale_out * rescale_in),\
(((scaled) + rescale_out * 10) % rescale_out) ),\
(1.0f - (((ntsc) + 100) & 2))
#else
#define PIXEL_OFFSET( ntsc, scaled ) \
(kernel_size / 2 + (ntsc) - (scaled)),\
(1.0f - (((ntsc) + 100) & 2))
#endif
extern pixel_info_t const atari_ntsc_pixels [alignment_count];
/* Generate pixel at all burst phases and column alignments */
static void gen_kernel( init_t* impl, float y, float i, float q, atari_ntsc_rgb_t* out )
{
/* generate for each scanline burst phase */
float const* to_rgb = impl->to_rgb;
int burst_remain = burst_count;
y -= rgb_offset;
do
{
/* Encode yiq into *two* composite signals (to allow control over artifacting).
Convolve these with kernels which: filter respective components, apply
sharpening, and rescale horizontally. Convert resulting yiq to rgb and pack
into integer. Based on algorithm by NewRisingSun. */
pixel_info_t const* pixel = atari_ntsc_pixels;
int alignment_remain = alignment_count;
do
{
/* negate is -1 when composite starts at odd multiple of 2 */
float const yy = y * impl->fringing * pixel->negate;
float const ic0 = (i + yy) * pixel->kernel [0];
float const qc1 = (q + yy) * pixel->kernel [1];
float const ic2 = (i - yy) * pixel->kernel [2];
float const qc3 = (q - yy) * pixel->kernel [3];
float const factor = impl->artifacts * pixel->negate;
float const ii = i * factor;
float const yc0 = (y + ii) * pixel->kernel [0];
float const yc2 = (y - ii) * pixel->kernel [2];
float const qq = q * factor;
float const yc1 = (y + qq) * pixel->kernel [1];
float const yc3 = (y - qq) * pixel->kernel [3];
float const* k = &impl->kernel [pixel->offset];
int n;
++pixel;
for ( n = rgb_kernel_size; n; --n )
{
float i = k[0]*ic0 + k[2]*ic2;
float q = k[1]*qc1 + k[3]*qc3;
float y = k[kernel_size+0]*yc0 + k[kernel_size+1]*yc1 +
k[kernel_size+2]*yc2 + k[kernel_size+3]*yc3 + rgb_offset;
if ( rescale_out <= 1 )
k--;
else if ( k < &impl->kernel [kernel_size * 2 * (rescale_out - 1)] )
k += kernel_size * 2 - 1;
else
k -= kernel_size * 2 * (rescale_out - 1) + 2;
{
int r, g, b = YIQ_TO_RGB( y, i, q, to_rgb, int, r, g );
*out++ = PACK_RGB( r, g, b ) - rgb_bias;
}
}
}
while ( alignment_count > 1 && --alignment_remain );
if ( burst_count <= 1 )
break;
to_rgb += 6;
ROTATE_IQ( i, q, -0.866025f, -0.5f ); /* -120 degrees */
}
while ( --burst_remain );
}
static void correct_errors( atari_ntsc_rgb_t color, atari_ntsc_rgb_t* out );
/* Atari change: adjust DISTRIBUTE_ERROR to 4/7 pixel ratio. */
#if DISABLE_CORRECTION
#define CORRECT_ERROR( a ) { out [i] += rgb_bias; }
#define DISTRIBUTE_ERROR( a, b, c, d ) { out [i] += rgb_bias; }
#else
#define CORRECT_ERROR( a ) { out [a] += error; }
#define DISTRIBUTE_ERROR( a, b, c, d ) {\
atari_ntsc_rgb_t fourth = (error + 2 * atari_ntsc_rgb_builder) >> 2;\
fourth &= (rgb_bias >> 1) - atari_ntsc_rgb_builder;\
fourth -= rgb_bias >> 2;\
out [a] += fourth;\
out [b] += fourth;\
out [c] += fourth;\
out [d] += fourth;\
out [i] += error - (fourth * 4);\
}
#endif
#define RGB_PALETTE_OUT( rgb, out_ )\
{\
unsigned char* out = (out_);\
atari_ntsc_rgb_t clamped = (rgb);\
ATARI_NTSC_CLAMP_( clamped, (8 - rgb_bits) );\
out [0] = (unsigned char) (clamped >> 21);\
out [1] = (unsigned char) (clamped >> 11);\
out [2] = (unsigned char) (clamped >> 1);\
}
/* blitter related */
#ifndef restrict
#if defined (__GNUC__)
#define restrict __restrict__
#elif defined (_MSC_VER) && _MSC_VER > 1300
#define restrict __restrict
#else
/* no support for restricted pointers */
#define restrict
#endif
#endif
#include <limits.h>
#if ATARI_NTSC_OUT_DEPTH <= 16
#if USHRT_MAX == 0xFFFF
typedef unsigned short atari_ntsc_out_t;
#else
#error "Need 16-bit int type"
#endif
#else
#if UINT_MAX == 0xFFFFFFFF
typedef unsigned int atari_ntsc_out_t;
#elif ULONG_MAX == 0xFFFFFFFF
typedef unsigned long atari_ntsc_out_t;
#else
#error "Need 32-bit int type"
#endif
#endif
#endif // DISPLAY_TV

View File

@ -1,7 +1,8 @@
MODULE := src/common/tv_filters MODULE := src/common/tv_filters
MODULE_OBJS := \ MODULE_OBJS := \
src/common/tv_filters/NTSCFilter.o src/common/tv_filters/NTSCFilter.o \
src/common/tv_filters/atari_ntsc.o
MODULE_DIRS += \ MODULE_DIRS += \
src/common/tv_filters src/common/tv_filters