mirror of https://github.com/stella-emu/stella.git
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:
parent
6836963e8b
commit
5d4c09da54
|
@ -25,28 +25,11 @@
|
|||
#include <cmath>
|
||||
#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()
|
||||
: myNTSCEmu(NULL),
|
||||
myCurrentModeNum(-1)
|
||||
: mySetup(atari_ntsc_composite),
|
||||
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 */
|
||||
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);
|
||||
mySetup = atari_ntsc_composite;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void NTSCFilter::atari_ntsc_blit( unsigned char const* in, long in_pitch,
|
||||
int width, int height, unsigned short* out, long out_pitch )
|
||||
void NTSCFilter::setPreset(int preset)
|
||||
{
|
||||
int const chunk_count = (width - 10) / 7;
|
||||
long next_in_line = in_pitch - chunk_count * 4;
|
||||
long next_out_line = out_pitch - (chunk_count + 1) * (7 * sizeof *out);
|
||||
while ( height-- )
|
||||
{
|
||||
#define ENTRY( n ) myNTSCEmu->table [n]
|
||||
ntsc_rgb_t const* k1 = ENTRY( 0 );
|
||||
ntsc_rgb_t const* k2 = k1;
|
||||
ntsc_rgb_t const* k3 = k1;
|
||||
ntsc_rgb_t const* k4 = k1;
|
||||
ntsc_rgb_t const* k5 = k4;
|
||||
ntsc_rgb_t const* k6 = k4;
|
||||
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);
|
||||
if (preset < PRESET_CUSTOM) {
|
||||
mySetup = *presets[preset];
|
||||
updateFilter();
|
||||
|
||||
#if 0 // FIXME - what are these items for??
|
||||
/* Copy settings from the preset to NTSC setup. */
|
||||
COLOURS_NTSC_specific_setup.hue = mySetup.hue;
|
||||
COLOURS_NTSC_setup.saturation = mySetup.saturation;
|
||||
COLOURS_NTSC_setup.contrast = mySetup.contrast;
|
||||
COLOURS_NTSC_setup.brightness = mySetup.brightness;
|
||||
COLOURS_NTSC_setup.gamma = mySetup.gamma;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void NTSCFilter::rotate_matrix( float const* in, float s, float c, float* out )
|
||||
int NTSCFilter::getPreset()
|
||||
{
|
||||
int n = 3;
|
||||
while ( n-- )
|
||||
{
|
||||
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 */
|
||||
// FIXME - for now just return composite
|
||||
return 0;
|
||||
#if 0
|
||||
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;
|
||||
ntsc->contrast = (setup->contrast-0.0f) * 0.4f + 1;
|
||||
ntsc->sharpness = 1 + (setup->sharpness < 0 ? setup->sharpness * 0.5f : setup->sharpness);
|
||||
|
||||
for ( i = 0; i < composite_size; i++ )
|
||||
ntsc->composite [i] = 0;
|
||||
|
||||
/* Generate gaussian kernel, padded with zero */
|
||||
for ( i = 0; i < ntsc_kernel_size; i++ )
|
||||
ntsc->kernel [i] = 0;
|
||||
for ( i = -composite_border; i <= composite_border; i++ )
|
||||
ntsc->kernel [ntsc_kernel_size / 2 + i] = exp( i * i * (-0.03125f * gaussian_factor) );
|
||||
|
||||
/* 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;
|
||||
|
||||
for (i = 0; i < PRESET_SIZE; i ++) {
|
||||
if (Util_almostequal(mySetup.sharpness, presets[i]->sharpness, 0.001) &&
|
||||
Util_almostequal(mySetup.resolution, presets[i]->resolution, 0.001) &&
|
||||
Util_almostequal(mySetup.artifacts, presets[i]->artifacts, 0.001) &&
|
||||
Util_almostequal(mySetup.fringing, presets[i]->fringing, 0.001) &&
|
||||
Util_almostequal(mySetup.bleed, presets[i]->bleed, 0.001) &&
|
||||
Util_almostequal(mySetup.burst_phase, presets[i]->burst_phase, 0.001) &&
|
||||
Util_almostequal(COLOURS_NTSC_specific_setup.hue, presets[i]->hue, 0.001) &&
|
||||
Util_almostequal(COLOURS_NTSC_setup.saturation, presets[i]->saturation, 0.001) &&
|
||||
Util_almostequal(COLOURS_NTSC_setup.contrast, presets[i]->contrast, 0.001) &&
|
||||
Util_almostequal(COLOURS_NTSC_setup.brightness, presets[i]->brightness, 0.001) &&
|
||||
Util_almostequal(COLOURS_NTSC_setup.gamma, presets[i]->gamma, 0.001))
|
||||
return i;
|
||||
}
|
||||
|
||||
/* adjust decoder matrix */
|
||||
{
|
||||
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 );
|
||||
return PRESET_CUSTOM;
|
||||
#endif
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
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];
|
||||
float f0 = ntsc->composite [offset];
|
||||
float f1 = ntsc->composite [offset + 1];
|
||||
float f2 = ntsc->composite [offset + 2];
|
||||
float f3 = ntsc->composite [offset + 3];
|
||||
int x = 0;
|
||||
while ( x < composite_size )
|
||||
{
|
||||
#define PIXEL( get_y ) \
|
||||
{\
|
||||
float i = kernel [ 0] * f0 + kernel [-2] * f2;\
|
||||
float q = kernel [-1] * f1 + kernel [-3] * f3;\
|
||||
float y = get_y;\
|
||||
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];\
|
||||
kernel++;\
|
||||
out [0] = (int) r;\
|
||||
out [1] = (int) g;\
|
||||
out [2] = (int) b;\
|
||||
out += 3;\
|
||||
}
|
||||
|
||||
PIXEL( i - ntsc->composite [x + 0] )
|
||||
PIXEL( q - ntsc->composite [x + 1] )
|
||||
PIXEL( ntsc->composite [x + 2] - i )
|
||||
PIXEL( ntsc->composite [x + 3] - q )
|
||||
x += 4;
|
||||
|
||||
#undef PIXEL
|
||||
}
|
||||
int preset = getPreset();
|
||||
|
||||
if (preset == PRESET_CUSTOM)
|
||||
preset = PRESET_COMPOSITE;
|
||||
else
|
||||
preset = (preset + 1) % PRESET_SIZE;
|
||||
setPreset(preset);
|
||||
}
|
||||
|
||||
#if 0 // FIXME
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
int NTSCFilter::FILTER_NTSC_ReadConfig(char *option, char *ptr)
|
||||
{
|
||||
if (strcmp(option, "FILTER_NTSC_SHARPNESS") == 0)
|
||||
return Util_sscandouble(ptr, &mySetup.sharpness);
|
||||
else if (strcmp(option, "FILTER_NTSC_RESOLUTION") == 0)
|
||||
return Util_sscandouble(ptr, &mySetup.resolution);
|
||||
else if (strcmp(option, "FILTER_NTSC_ARTIFACTS") == 0)
|
||||
return Util_sscandouble(ptr, &mySetup.artifacts);
|
||||
else if (strcmp(option, "FILTER_NTSC_FRINGING") == 0)
|
||||
return Util_sscandouble(ptr, &mySetup.fringing);
|
||||
else if (strcmp(option, "FILTER_NTSC_BLEED") == 0)
|
||||
return Util_sscandouble(ptr, &mySetup.bleed);
|
||||
else if (strcmp(option, "FILTER_NTSC_BURST_PHASE") == 0)
|
||||
return Util_sscandouble(ptr, &mySetup.burst_phase);
|
||||
else
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void NTSCFilter::rescale( short const* in, int count, short* out )
|
||||
void NTSCFilter::FILTER_NTSC_WriteConfig(FILE *fp)
|
||||
{
|
||||
do
|
||||
{
|
||||
int const accuracy = 16;
|
||||
int const unit = 1 << accuracy;
|
||||
int const step = unit / 8;
|
||||
int left = unit - step;
|
||||
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 );
|
||||
fprintf(fp, "FILTER_NTSC_SHARPNESS=%g\n", mySetup.sharpness);
|
||||
fprintf(fp, "FILTER_NTSC_RESOLUTION=%g\n", mySetup.resolution);
|
||||
fprintf(fp, "FILTER_NTSC_ARTIFACTS=%g\n", mySetup.artifacts);
|
||||
fprintf(fp, "FILTER_NTSC_FRINGING=%g\n", mySetup.fringing);
|
||||
fprintf(fp, "FILTER_NTSC_BLEED=%g\n", mySetup.bleed);
|
||||
fprintf(fp, "FILTER_NTSC_BURST_PHASE=%g\n", mySetup.burst_phase);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
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 const accuracy = 16;
|
||||
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++;
|
||||
}
|
||||
int i;
|
||||
int j;
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
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++) {
|
||||
if (strcmp(argv[i], "-ntsc_hue") == 0) {
|
||||
atari_ntsc_setup->hue = atof(argv[++i]);
|
||||
}else if (strcmp(argv[i], "-ntsc_sat") == 0){
|
||||
atari_ntsc_setup->saturation = atof(argv[++i]);
|
||||
}else if (strcmp(argv[i], "-ntsc_cont") == 0){
|
||||
atari_ntsc_setup->contrast = atof(argv[++i]);
|
||||
}else if (strcmp(argv[i], "-ntsc_bright") == 0){
|
||||
atari_ntsc_setup->brightness = atof(argv[++i]);
|
||||
}else if (strcmp(argv[i], "-ntsc_sharp") == 0){
|
||||
atari_ntsc_setup->sharpness = atof(argv[++i]);
|
||||
}else if (strcmp(argv[i], "-ntsc_burst") == 0){
|
||||
atari_ntsc_setup->burst_phase = atof(argv[++i]);
|
||||
}else if (strcmp(argv[i], "-ntsc_gauss") == 0){
|
||||
atari_ntsc_setup->gaussian_factor = atof(argv[++i]);
|
||||
}else if (strcmp(argv[i], "-ntsc_gamma") == 0){
|
||||
atari_ntsc_setup->gamma_adj = atof(argv[++i]);
|
||||
}else if (strcmp(argv[i], "-ntsc_ramp") == 0){
|
||||
atari_ntsc_setup->saturation_ramp = atof(argv[++i]);
|
||||
int i_a = (i + 1 < *argc); /* is argument available? */
|
||||
int a_m = FALSE; /* error, argument missing! */
|
||||
|
||||
if (strcmp(argv[i], "-ntsc-sharpness") == 0) {
|
||||
if (i_a)
|
||||
mySetup.sharpness = atof(argv[++i]);
|
||||
else a_m = TRUE;
|
||||
}
|
||||
else if (strcmp(argv[i], "-ntsc-resolution") == 0) {
|
||||
if (i_a)
|
||||
mySetup.resolution = atof(argv[++i]);
|
||||
else a_m = TRUE;
|
||||
}
|
||||
else if (strcmp(argv[i], "-ntsc-artifacts") == 0) {
|
||||
if (i_a)
|
||||
mySetup.artifacts = atof(argv[++i]);
|
||||
else a_m = TRUE;
|
||||
}
|
||||
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 {
|
||||
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_sat <n> Set NTSC saturation (default %.2g) (-ntsc_emu only)",atari_ntsc_setup->saturation);
|
||||
Log_print("\t-ntsc_cont <n> Set NTSC contrast (default %.2g) (-ntsc_emu only)",atari_ntsc_setup->contrast);
|
||||
Log_print("\t-ntsc_bright <n> Set NTSC brightness (default %.2g) (-ntsc_emu only)",atari_ntsc_setup->brightness);
|
||||
Log_print("\t-ntsc_sharp <n> Set NTSC sharpness (default %.2g) (-ntsc_emu only)",atari_ntsc_setup->sharpness);
|
||||
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_emu only)");
|
||||
Log_print("\t-ntsc_gauss <n> Set NTSC Gaussian factor (default %.2g)",atari_ntsc_setup->gaussian_factor);
|
||||
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)");
|
||||
Log_print("\t-ntsc-sharpness <n> Set sharpness for NTSC filter (default %.2g)", mySetup.sharpness);
|
||||
Log_print("\t-ntsc-resolution <n> Set resolution for NTSC filter (default %.2g)", mySetup.resolution);
|
||||
Log_print("\t-ntsc-artifacts <n> Set luma artifacts ratio for NTSC filter (default %.2g)", mySetup.artifacts);
|
||||
Log_print("\t-ntsc-fringing <n> Set chroma fringing ratio for NTSC filter (default %.2g)", mySetup.fringing);
|
||||
Log_print("\t-ntsc-bleed <n> Set bleed for NTSC filter (default %.2g)", mySetup.bleed);
|
||||
Log_print("\t-ntsc-burstphase <n> Set burst phase (artifact colours) for NTSC filter (default %.2g)", mySetup.burst_phase);
|
||||
Log_print("\t-ntsc-filter-preset composite|svideo|rgb|monochrome");
|
||||
Log_print("\t Use one of predefined NTSC filter adjustments");
|
||||
}
|
||||
|
||||
argv[j++] = argv[i];
|
||||
}
|
||||
|
||||
if (a_m) {
|
||||
Log_print("Missing argument for '%s'", argv[i]);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
*argc = j;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
#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
|
||||
|
|
|
@ -24,21 +24,27 @@
|
|||
|
||||
#include "bspf.hxx"
|
||||
#include "Array.hxx"
|
||||
#include "atari_ntsc.h"
|
||||
|
||||
// Although the blitter supports 15 and 16 bit, we always use 16-bit
|
||||
#ifndef ATARI_NTSC_RGB_BITS
|
||||
#define ATARI_NTSC_RGB_BITS 16
|
||||
#endif
|
||||
|
||||
// Limits for the adjustable values.
|
||||
#define FILTER_NTSC_SHARPNESS_MIN -1.0
|
||||
#define FILTER_NTSC_SHARPNESS_MAX 1.0
|
||||
#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
|
||||
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."
|
||||
Original code based on implementation from http://www.slack.net/~ant.
|
||||
|
||||
Atari TIA NTSC composite video to RGB emulator/blitter.
|
||||
*/
|
||||
|
@ -48,112 +54,61 @@ class NTSCFilter
|
|||
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:
|
||||
/**
|
||||
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();
|
||||
|
||||
/* 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);
|
||||
static void updateYIQTable(double yiq_table[768], double start_angle);
|
||||
|
||||
private:
|
||||
/* Picture parameters, ranging from -1.0 to 1.0 where 0.0 is normal. To easily
|
||||
clear all fields, make it a static object then set whatever fields you want:
|
||||
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
|
||||
};
|
||||
// Pointer to the NTSC filter structure
|
||||
atari_ntsc_t myFilter;
|
||||
|
||||
enum {
|
||||
atari_ntsc_entry_size = 56,
|
||||
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;
|
||||
// Contains controls used to adjust the palette in the NTSC filter
|
||||
atari_ntsc_setup_t mySetup;
|
||||
|
||||
int myCurrentModeNum;
|
||||
Common::Array<atari_ntsc_setup_t> myModeList;
|
||||
|
||||
private:
|
||||
/* Initialize and adjust parameters. Can be called multiple times on the same
|
||||
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 );
|
||||
static atari_ntsc_setup_t const * const presets[PRESET_SIZE];
|
||||
static char const * const preset_cfg_strings[PRESET_SIZE];
|
||||
};
|
||||
|
||||
#endif // DISPLAY_TV
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -1,7 +1,8 @@
|
|||
MODULE := src/common/tv_filters
|
||||
|
||||
MODULE_OBJS := \
|
||||
src/common/tv_filters/NTSCFilter.o
|
||||
src/common/tv_filters/NTSCFilter.o \
|
||||
src/common/tv_filters/atari_ntsc.o
|
||||
|
||||
MODULE_DIRS += \
|
||||
src/common/tv_filters
|
||||
|
|
Loading…
Reference in New Issue