From 0f2df7e87b1b0f48a29c2e94ebfc4788b6ce3397 Mon Sep 17 00:00:00 2001 From: Stephen Anthony Date: Sat, 17 Jun 2017 20:08:36 -0230 Subject: [PATCH] Fairly large reorganization of the Blargg NTSC TV effects code: - converted many pointers to references - merged code from several files into one class - broke up some methods into more managable chunks This will allow it to be easy to add the phosphor code during NTSC TV emulation. --- src/common/tv_filters/AtariNTSC.cxx | 428 +++++++++++++++++++++ src/common/tv_filters/AtariNTSC.hxx | 278 ++++++++++++++ src/common/tv_filters/NTSCFilter.cxx | 22 +- src/common/tv_filters/NTSCFilter.hxx | 26 +- src/common/tv_filters/atari_ntsc.cxx | 205 ---------- src/common/tv_filters/atari_ntsc.hxx | 157 -------- src/common/tv_filters/atari_ntsc_impl.hxx | 432 ---------------------- src/common/tv_filters/module.mk | 4 +- src/emucore/TIASurface.cxx | 4 +- 9 files changed, 730 insertions(+), 826 deletions(-) create mode 100644 src/common/tv_filters/AtariNTSC.cxx create mode 100644 src/common/tv_filters/AtariNTSC.hxx delete mode 100644 src/common/tv_filters/atari_ntsc.cxx delete mode 100644 src/common/tv_filters/atari_ntsc.hxx delete mode 100644 src/common/tv_filters/atari_ntsc_impl.hxx diff --git a/src/common/tv_filters/AtariNTSC.cxx b/src/common/tv_filters/AtariNTSC.cxx new file mode 100644 index 000000000..462c13002 --- /dev/null +++ b/src/common/tv_filters/AtariNTSC.cxx @@ -0,0 +1,428 @@ +//============================================================================ +// +// 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-2017 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. +//============================================================================ + +#include "AtariNTSC.hxx" + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void AtariNTSC::initialize(const Setup& setup, const uInt8* palette) +{ + init(myImpl, setup); + initializePalette(palette); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void AtariNTSC::initializePalette(const uInt8* palette) +{ + // Palette stores R/G/B data for 'palette_size' entries + for ( uInt32 entry = 0; entry < palette_size; ++entry ) + { + float r = myImpl.to_float [*palette++]; + float g = myImpl.to_float [*palette++]; + float b = myImpl.to_float [*palette++]; + + float y, i, q = RGB_TO_YIQ( r, g, b, y, i ); + + // Generate kernel + int ir, ig, ib = YIQ_TO_RGB( y, i, q, myImpl.to_rgb, int, ir, ig ); + uInt32 rgb = PACK_RGB( ir, ig, ib ); + + uInt32* kernel = myNTSC.table[entry]; + genKernel(myImpl, y, i, q, kernel); + + for ( uInt32 i = 0; i < rgb_kernel_size / 2; i++ ) + { + uInt32 error = rgb - + kernel [i ] - kernel [(i+10)%14+14] - + kernel [i + 7] - kernel [i + 3 +14]; + kernel [i + 3 + 14] += error; + } + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void AtariNTSC::blitSingle(const uInt8* atari_in, uInt32 in_width, + uInt32 in_height, void* rgb_out, uInt32 out_pitch) +{ + uInt32 const chunk_count = (in_width - 1) / AN_in_chunk; + while ( in_height-- ) + { + const uInt8* line_in = atari_in; + ATARI_NTSC_BEGIN_ROW( &myNTSC, AN_black, line_in[0] ); + uInt32* restrict line_out = static_cast(rgb_out); + ++line_in; + + for ( uInt32 n = chunk_count; n; --n ) + { + /* order of input and output pixels must not be altered */ + ATARI_NTSC_COLOR_IN( 0, &myNTSC, line_in[0] ); + ATARI_NTSC_RGB_OUT_8888( 0, line_out[0] ); + ATARI_NTSC_RGB_OUT_8888( 1, line_out[1] ); + ATARI_NTSC_RGB_OUT_8888( 2, line_out[2] ); + ATARI_NTSC_RGB_OUT_8888( 3, line_out[3] ); + + ATARI_NTSC_COLOR_IN( 1, &myNTSC, line_in[1] ); + ATARI_NTSC_RGB_OUT_8888( 4, line_out[4] ); + ATARI_NTSC_RGB_OUT_8888( 5, line_out[5] ); + ATARI_NTSC_RGB_OUT_8888( 6, line_out[6] ); + + line_in += 2; + line_out += 7; + } + + /* finish final pixels */ + ATARI_NTSC_COLOR_IN( 0, &myNTSC, AN_black ); + ATARI_NTSC_RGB_OUT_8888( 0, line_out[0] ); + ATARI_NTSC_RGB_OUT_8888( 1, line_out[1] ); + ATARI_NTSC_RGB_OUT_8888( 2, line_out[2] ); + ATARI_NTSC_RGB_OUT_8888( 3, line_out[3] ); + + ATARI_NTSC_COLOR_IN( 1, &myNTSC, AN_black ); + ATARI_NTSC_RGB_OUT_8888( 4, line_out[4] ); + ATARI_NTSC_RGB_OUT_8888( 5, line_out[5] ); + ATARI_NTSC_RGB_OUT_8888( 6, line_out[6] ); + + atari_in += in_width; + rgb_out = static_cast(rgb_out) + out_pitch; + } +} + +#if 0 +void atari_ntsc_blit_double( atari_ntsc_t const* ntsc, + atari_ntsc_in_t const* atari_in1, atari_ntsc_in_t const* atari_in2, + uInt32 in_width, uInt32 in_height, void* rgb_out, uInt32 out_pitch ) +{ + #define TO_DOUBLE(pixel1, pixel2) (((pixel1>>1)<<7)+(pixel2>>1)) + + uInt32 const chunk_count = (in_width - 1) / atari_ntsc_in_chunk; + while ( in_height-- ) + { + atari_ntsc_in_t const* line_in1 = atari_in1; + atari_ntsc_in_t const* line_in2 = atari_in2; + ATARI_NTSC_BEGIN_ROW( ntsc, + TO_DOUBLE(atari_ntsc_black, atari_ntsc_black), + TO_DOUBLE(line_in1[0], line_in2[0]) ); + uInt32* restrict line_out = static_cast(rgb_out); + ++line_in1; + ++line_in2; + + for ( uInt32 n = chunk_count; n; --n ) + { + /* order of input and output pixels must not be altered */ + ATARI_NTSC_COLOR_IN( 0, ntsc, + TO_DOUBLE(line_in1[0], line_in2[0]) ); + ATARI_NTSC_RGB_OUT_8888( 0, line_out[0] ); + ATARI_NTSC_RGB_OUT_8888( 1, line_out[1] ); + ATARI_NTSC_RGB_OUT_8888( 2, line_out[2] ); + ATARI_NTSC_RGB_OUT_8888( 3, line_out[3] ); + + ATARI_NTSC_COLOR_IN( 1, ntsc, + TO_DOUBLE(line_in1[1], line_in2[1]) ); + ATARI_NTSC_RGB_OUT_8888( 4, line_out[4] ); + ATARI_NTSC_RGB_OUT_8888( 5, line_out[5] ); + ATARI_NTSC_RGB_OUT_8888( 6, line_out[6] ); + + line_in1 += 2; + line_in2 += 2; + line_out += 7; + } + + /* finish final pixels */ + ATARI_NTSC_COLOR_IN( 0, ntsc, + TO_DOUBLE(atari_ntsc_black, atari_ntsc_black) ); + ATARI_NTSC_RGB_OUT_8888( 0, line_out[0] ); + ATARI_NTSC_RGB_OUT_8888( 1, line_out[1] ); + ATARI_NTSC_RGB_OUT_8888( 2, line_out[2] ); + ATARI_NTSC_RGB_OUT_8888( 3, line_out[3] ); + + ATARI_NTSC_COLOR_IN( 1, ntsc, + TO_DOUBLE(atari_ntsc_black, atari_ntsc_black) ); + ATARI_NTSC_RGB_OUT_8888( 4, line_out[4] ); + ATARI_NTSC_RGB_OUT_8888( 5, line_out[5] ); + ATARI_NTSC_RGB_OUT_8888( 6, line_out[6] ); + + atari_in1 += in_width; + atari_in2 += in_width; + rgb_out = static_cast(rgb_out) + out_pitch; + } +} +#endif + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void AtariNTSC::init(init_t& impl, const Setup& setup) +{ + impl.brightness = float(setup.brightness) * (0.5f * rgb_unit) + rgb_offset; + impl.contrast = float(setup.contrast) * (0.5f * rgb_unit) + rgb_unit; + + 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; + + initFilters(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 */ + { + float hue = float(setup.hue) * PI + PI / 180 * ext_decoder_hue; + float sat = float(setup.saturation) + 1; + float const* decoder = setup.decoder_matrix; + if ( !decoder ) + { + decoder = default_decoder; + 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; // FIXME: dead code detected by llvm scan-build + do + { + float const* in = decoder; + int n2 = 3; + do + { + float i = *in++; + float q = *in++; + *out++ = i * c - q * s; + *out++ = i * s + q * c; + } + while ( --n2 ); + if ( burst_count <= 1 ) + break; + ROTATE_IQ( s, c, 0.866025f, -0.5f ); /* +120 degrees */ + } + while ( --n ); + } + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void AtariNTSC::initFilters(init_t& impl, const Setup& setup) +{ + float kernels [kernel_size * 2]; + + /* generate luma (y) filter using sinc kernel */ + { + /* sinc with rolloff (dsf) */ + float const rolloff = 1 + float(setup.sharpness) * 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 > 1.056 || pow_a_n < 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 - 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; + } + } + + /* 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; + } + } + } + + /* generate linear rescale kernels */ + 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 ); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// Generate pixel at all burst phases and column alignments +void AtariNTSC::genKernel(init_t& impl, float y, float i, float q, uInt32* 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 fi = k[0]*ic0 + k[2]*ic2; + float fq = k[1]*qc1 + k[3]*qc3; + float fy = k[kernel_size+0]*yc0 + k[kernel_size+1]*yc1 + + k[kernel_size+2]*yc2 + k[kernel_size+3]*yc3 + rgb_offset; + 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( fy, fi, fq, 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 ); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +const AtariNTSC::Setup AtariNTSC::TV_Composite = { + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.15, 0.0, 0.0, 0.0, 0 +}; +const AtariNTSC::Setup AtariNTSC::TV_SVideo = { + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.45, -1.0, -1.0, 0.0, 0 +}; +const AtariNTSC::Setup AtariNTSC::TV_RGB = { + 0.0, 0.0, 0.0, 0.0, 0.2, 0.0, 0.70, -1.0, -1.0, -1.0, 0 +}; +const AtariNTSC::Setup AtariNTSC::TV_Bad = { + 0.1, -0.3, 0.3, 0.25, 0.2, 0.0, 0.1, 0.5, 0.5, 0.5, 0 +}; + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +const AtariNTSC::pixel_info_t AtariNTSC::atari_ntsc_pixels[alignment_count] = { + { PIXEL_OFFSET( -4, -9 ), { 1, 1, 1, 1 } }, + { PIXEL_OFFSET( 0, -5 ), { 1, 1, 1, 1 } }, +}; + +const float AtariNTSC::default_decoder[6] = { + 0.9563f, 0.6210f, -0.2721f, -0.6474f, -1.1070f, 1.7046f +}; diff --git a/src/common/tv_filters/AtariNTSC.hxx b/src/common/tv_filters/AtariNTSC.hxx new file mode 100644 index 000000000..a0ba18565 --- /dev/null +++ b/src/common/tv_filters/AtariNTSC.hxx @@ -0,0 +1,278 @@ +//============================================================================ +// +// 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-2017 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. +//============================================================================ + +/* + * Atari TIA NTSC video filter + * Based on nes_ntsc 0.2.2. http://www.slack.net/~ant + * + * Copyright (C) 2006-2009 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 + */ + +#ifndef ATARI_NTSC_HXX +#define ATARI_NTSC_HXX + +#include +#include "bspf.hxx" + +class AtariNTSC +{ + public: + enum { + palette_size = 256, + entry_size = 2 * 14, + }; + + // Image parameters, ranging from -1.0 to 1.0. Actual internal values shown + // in parenthesis and should remain fairly stable in future versions. + struct Setup // 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) + float const* decoder_matrix; // optional RGB decoder matrix, 6 elements + }; + + // Video format presets + static const Setup TV_Composite; // color bleeding + artifacts + static const Setup TV_SVideo; // color bleeding only + static const Setup TV_RGB; // crisp image + static const Setup TV_Bad; // badly adjusted TV + + // Initializes and adjusts parameters. + void initialize(const Setup& setup, const uInt8* palette); + void initializePalette(const uInt8* palette); + + // Filters one or more rows of pixels. Input pixels are 8-bit Atari + // palette colors. + // 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. + void blitSingle(const uInt8* atari_in, uInt32 in_width, uInt32 in_height, + void* rgb_out, uInt32 out_pitch); + + // Number of output pixels written by blitter for given input width. + // Width might be rounded down slightly; use inWidth() on result to + // find rounded value. Guaranteed not to round 160 down at all. + static uInt32 outWidth(uInt32 in_width) { + return ((((in_width) - 1) / AN_in_chunk + 1)* AN_out_chunk); + } + + // Number of input pixels that will fit within given output width. + // Might be rounded down slightly; use outWidth() on result to find + // rounded value. + static uInt32 inWidth( uInt32 out_width ) { + return (((out_width) / AN_out_chunk - 1) * AN_in_chunk + 1); + } + + private: + enum { + AN_in_chunk = 2, // number of input pixels read per chunk + AN_out_chunk = 7, // number of output pixels generated per chunk + AN_black = 0, // palette index for black + + alignment_count = 2, + burst_count = 1, + rescale_in = 8, + rescale_out = 7, + + burst_size = entry_size / burst_count, + kernel_half = 16, + kernel_size = kernel_half * 2 + 1, + gamma_size = 256, + + rgb_builder = ((1 << 21) | (1 << 11) | (1 << 1)), + rgb_kernel_size = burst_size / alignment_count, + rgb_bits = 8, + rgb_unit = (1 << rgb_bits), + rgb_bias = rgb_unit * 2 * rgb_builder, + + std_decoder_hue = 0, + ext_decoder_hue = std_decoder_hue + 15 + }; + + #define artifacts_mid 1.5f + #define artifacts_max 2.5f + #define fringing_mid 1.0f + #define fringing_max 2.0f + #define rgb_offset (rgb_unit * 2 + 0.5f) + + #undef PI + #define PI 3.14159265358979323846f + #define LUMA_CUTOFF 0.20 + + struct atari_ntsc_t { + uInt32 table[palette_size][entry_size]; + }; + atari_ntsc_t myNTSC; + + 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 myImpl; + + struct pixel_info_t + { + int offset; + float negate; + float kernel [4]; + }; + static const pixel_info_t atari_ntsc_pixels[alignment_count]; + + static const float default_decoder[6]; + + void init(init_t& impl, const Setup& setup); + void initFilters(init_t& impl, const Setup& setup); + // Generate pixel at all burst phases and column alignments + void genKernel(init_t& impl, float y, float i, float q, uInt32* out); + + // Begins outputting row and starts two pixels. First pixel will be cut + // off a bit. Use atari_ntsc_black for unused pixels. + #define ATARI_NTSC_BEGIN_ROW( ntsc, pixel0, pixel1 ) \ + ATARI_NTSC_BEGIN_ROW_6_( pixel0, pixel1, ATARI_NTSC_ENTRY_, ntsc ) + + // Begins input pixel + #define ATARI_NTSC_COLOR_IN( in_index, ntsc, color_in ) \ + ATARI_NTSC_COLOR_IN_( in_index, color_in, ATARI_NTSC_ENTRY_, ntsc ) + + // Generates output in the specified 32-bit format (x = junk bits). + // native: xxxRRRRR RRRxxGGG GGGGGxxB BBBBBBBx (native internal format) + // 8888: 00000000 RRRRRRRR GGGGGGGG BBBBBBBB (8-8-8-8 32-bit ARGB) + #define ATARI_NTSC_RGB_OUT_8888( index, rgb_out ) {\ + uInt32 raw_ =\ + kernel0 [index ] + kernel1 [(index+10)%7+14] +\ + kernelx0 [(index+7)%14] + kernelx1 [(index+ 3)%7+14+7];\ + ATARI_NTSC_CLAMP_( raw_, 0 );\ + rgb_out = (raw_>>5 & 0x00FF0000)|(raw_>>3 & 0x0000FF00)|(raw_>>1 & 0x000000FF);\ + } + + #define ATARI_NTSC_ENTRY_( ntsc, n ) (ntsc)->table [n] + + // common 3->7 ntsc macros + #define ATARI_NTSC_BEGIN_ROW_6_( pixel0, pixel1, ENTRY, table ) \ + unsigned const atari_ntsc_pixel0_ = (pixel0);\ + uInt32 const* kernel0 = ENTRY( table, atari_ntsc_pixel0_ );\ + unsigned const atari_ntsc_pixel1_ = (pixel1);\ + uInt32 const* kernel1 = ENTRY( table, atari_ntsc_pixel1_ );\ + uInt32 const* kernelx0;\ + uInt32 const* kernelx1 = kernel0 + + // common ntsc macros + #define atari_ntsc_clamp_mask (rgb_builder * 3 / 2) + #define atari_ntsc_clamp_add (rgb_builder * 0x101) + #define ATARI_NTSC_CLAMP_( io, shift ) {\ + uInt32 sub = (io) >> (9-(shift)) & atari_ntsc_clamp_mask;\ + uInt32 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_ ));\ + } + + // kernel generation + #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;\ + } + #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)\ + ) + #ifndef PACK_RGB + #define PACK_RGB( r, g, b ) ((r) << 21 | (g) << 11 | (b) << 1) + #endif + + #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)) + + #define DISTRIBUTE_ERROR( a, b, c ) {\ + uInt32 fourth = (error + 2 * rgb_builder) >> 2;\ + fourth &= (rgb_bias >> 1) - rgb_builder;\ + fourth -= rgb_bias >> 2;\ + out [a] += fourth;\ + out [b] += fourth;\ + out [c] += fourth;\ + out [i] += error - (fourth * 3);\ + } + + #define RGB_PALETTE_OUT( rgb, out_ )\ + {\ + unsigned char* out = (out_);\ + uInt32 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 +}; + +#endif diff --git a/src/common/tv_filters/NTSCFilter.cxx b/src/common/tv_filters/NTSCFilter.cxx index 0c8bc4d32..8e984adab 100644 --- a/src/common/tv_filters/NTSCFilter.cxx +++ b/src/common/tv_filters/NTSCFilter.cxx @@ -22,7 +22,7 @@ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - NTSCFilter::NTSCFilter() - : mySetup(atari_ntsc_composite), + : mySetup(AtariNTSC::TV_Composite), myPreset(PRESET_OFF), myCurrentAdjustable(0) { @@ -76,19 +76,19 @@ string NTSCFilter::setPreset(Preset preset) switch(myPreset) { case PRESET_COMPOSITE: - mySetup = atari_ntsc_composite; + mySetup = AtariNTSC::TV_Composite; msg = "COMPOSITE"; break; case PRESET_SVIDEO: - mySetup = atari_ntsc_svideo; + mySetup = AtariNTSC::TV_SVideo; msg = "S-VIDEO"; break; case PRESET_RGB: - mySetup = atari_ntsc_rgb; + mySetup = AtariNTSC::TV_RGB; msg = "RGB"; break; case PRESET_BAD: - mySetup = atari_ntsc_bad; + mySetup = AtariNTSC::TV_Bad; msg = "BAD ADJUST"; break; case PRESET_CUSTOM: @@ -220,13 +220,13 @@ void NTSCFilter::getAdjustables(Adjustable& adjustable, Preset preset) const switch(preset) { case PRESET_COMPOSITE: - convertToAdjustable(adjustable, atari_ntsc_composite); break; + convertToAdjustable(adjustable, AtariNTSC::TV_Composite); break; case PRESET_SVIDEO: - convertToAdjustable(adjustable, atari_ntsc_svideo); break; + convertToAdjustable(adjustable, AtariNTSC::TV_SVideo); break; case PRESET_RGB: - convertToAdjustable(adjustable, atari_ntsc_rgb); break; + convertToAdjustable(adjustable, AtariNTSC::TV_RGB); break; case PRESET_BAD: - convertToAdjustable(adjustable, atari_ntsc_bad); break; + convertToAdjustable(adjustable, AtariNTSC::TV_Bad); break; case PRESET_CUSTOM: convertToAdjustable(adjustable, myCustomSetup); break; default: @@ -251,7 +251,7 @@ void NTSCFilter::setCustomAdjustables(Adjustable& adjustable) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void NTSCFilter::convertToAdjustable(Adjustable& adjustable, - const atari_ntsc_setup_t& setup) const + const AtariNTSC::Setup& setup) const { adjustable.hue = SCALE_TO_100(setup.hue); adjustable.saturation = SCALE_TO_100(setup.saturation); @@ -266,7 +266,7 @@ void NTSCFilter::convertToAdjustable(Adjustable& adjustable, } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -atari_ntsc_setup_t NTSCFilter::myCustomSetup = atari_ntsc_composite; +AtariNTSC::Setup NTSCFilter::myCustomSetup = AtariNTSC::TV_Composite; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - const NTSCFilter::AdjustableTag NTSCFilter::ourCustomAdjustables[10] = { diff --git a/src/common/tv_filters/NTSCFilter.hxx b/src/common/tv_filters/NTSCFilter.hxx index c2b201e56..aa2472190 100644 --- a/src/common/tv_filters/NTSCFilter.hxx +++ b/src/common/tv_filters/NTSCFilter.hxx @@ -22,7 +22,7 @@ class TIASurface; class Settings; #include "bspf.hxx" -#include "atari_ntsc.hxx" +#include "AtariNTSC.hxx" #define SCALE_FROM_100(x) ((x/50.0)-1.0) #define SCALE_TO_100(x) uInt32(50*(x+1.0)) @@ -76,7 +76,7 @@ class NTSCFilter // have changed) inline void updateFilter() { - atari_ntsc_init(&myFilter, &mySetup, myTIAPalette); + myNTSC.initialize(mySetup, myTIAPalette); } // Get adjustables for the given preset @@ -111,33 +111,25 @@ class NTSCFilter inline void blit_single(uInt8* src_buf, uInt32 src_width, uInt32 src_height, uInt32* dest_buf, uInt32 dest_pitch) { - atari_ntsc_blit_single(&myFilter, src_buf, src_width, src_height, - dest_buf, dest_pitch); - } - inline void blit_double(uInt8* src_buf, uInt8* src_back_buf, - uInt32 src_width, uInt32 src_height, - uInt32* dest_buf, uInt32 dest_pitch) - { - atari_ntsc_blit_double(&myFilter, src_buf, src_back_buf, src_width, - src_height, dest_buf, dest_pitch); + myNTSC.blitSingle(src_buf, src_width, src_height, dest_buf, dest_pitch); } private: // Convert from atari_ntsc_setup_t values to equivalent adjustables void convertToAdjustable(Adjustable& adjustable, - const atari_ntsc_setup_t& setup) const; + const AtariNTSC::Setup& setup) const; private: - // The NTSC filter structure - atari_ntsc_t myFilter; + // The NTSC object + AtariNTSC myNTSC; // Contains controls used to adjust the palette in the NTSC filter // This is the main setup object used by the underlying ntsc code - atari_ntsc_setup_t mySetup; + AtariNTSC::Setup mySetup; // This setup is used only in custom mode (after it is modified, // it is copied to mySetup) - static atari_ntsc_setup_t myCustomSetup; + static AtariNTSC::Setup myCustomSetup; // Current preset in use Preset myPreset; @@ -152,7 +144,7 @@ class NTSCFilter // 128x128 in first bytes of array // 128 in last bytes of array // Each colour is represented by 3 bytes, in R,G,B order - uInt8 myTIAPalette[atari_ntsc_palette_size * 3]; + uInt8 myTIAPalette[AtariNTSC::palette_size * 3]; struct AdjustableTag { const char* type; diff --git a/src/common/tv_filters/atari_ntsc.cxx b/src/common/tv_filters/atari_ntsc.cxx deleted file mode 100644 index 38e97ec93..000000000 --- a/src/common/tv_filters/atari_ntsc.cxx +++ /dev/null @@ -1,205 +0,0 @@ -//============================================================================ -// -// 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-2017 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. -//============================================================================ - -#include "atari_ntsc.hxx" - -/* Copyright (C) 2006-2009 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_ntsc_setup_t const atari_ntsc_composite = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.15, 0.0, 0.0, 0.0, 0 }; -atari_ntsc_setup_t const atari_ntsc_svideo = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.45, -1.0, -1.0, 0.0, 0 }; -atari_ntsc_setup_t const atari_ntsc_rgb = { 0.0, 0.0, 0.0, 0.0, 0.2, 0.0, 0.70, -1.0, -1.0, -1.0, 0 }; -atari_ntsc_setup_t const atari_ntsc_bad = { 0.1, -0.3, 0.3, 0.25, 0.2, 0.0, 0.1, 0.5, 0.5, 0.5, 0 }; - -#define alignment_count 2 -#define burst_count 1 -#define rescale_in 8 -#define rescale_out 7 - -#define artifacts_mid 1.5f -#define artifacts_max 2.5f -#define fringing_mid 1.0f -#define std_decoder_hue 0 - -#define gamma_size 256 - -#include "atari_ntsc_impl.hxx" - -/* 2 input pixels -> 8 composite samples */ -pixel_info_t const atari_ntsc_pixels [alignment_count] = { - { PIXEL_OFFSET( -4, -9 ), { 1, 1, 1, 1 } }, - { PIXEL_OFFSET( 0, -5 ), { 1, 1, 1, 1 } }, -}; - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -static void correct_errors( atari_ntsc_rgb_t color, atari_ntsc_rgb_t* out ) -{ - for ( uInt32 i = 0; i < rgb_kernel_size / 2; i++ ) - { - atari_ntsc_rgb_t error = color - - out [i ] - out [(i+10)%14+14] - - out [i + 7] - out [i + 3 +14]; - CORRECT_ERROR( i + 3 + 14 ); - } -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void atari_ntsc_init( atari_ntsc_t* ntsc, atari_ntsc_setup_t const* setup, - atari_ntsc_in_t const* palette ) -{ - init_t impl; - if ( !setup ) - setup = &atari_ntsc_composite; - init( &impl, setup ); - - // Palette stores R/G/B data for 'atari_ntsc_palette_size' entries - for ( uInt32 entry = 0; entry < atari_ntsc_palette_size; ++entry ) - { - float r = impl.to_float [*palette++]; - float g = impl.to_float [*palette++]; - float b = impl.to_float [*palette++]; - - float y, i, q = RGB_TO_YIQ( r, g, b, y, i ); - - // Generate kernel - int ir, ig, ib = YIQ_TO_RGB( y, i, q, impl.to_rgb, int, ir, ig ); - atari_ntsc_rgb_t rgb = PACK_RGB( ir, ig, ib ); - - if ( ntsc ) - { - atari_ntsc_rgb_t* kernel = ntsc->table [entry]; - gen_kernel( &impl, y, i, q, kernel ); - correct_errors( rgb, kernel ); - } - } -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void atari_ntsc_blit_single( atari_ntsc_t const* ntsc, - atari_ntsc_in_t const* atari_in, uInt32 in_width, uInt32 in_height, - void* rgb_out, uInt32 out_pitch ) -{ - uInt32 const chunk_count = (in_width - 1) / atari_ntsc_in_chunk; - while ( in_height-- ) - { - atari_ntsc_in_t const* line_in = atari_in; - ATARI_NTSC_BEGIN_ROW( ntsc, atari_ntsc_black, line_in[0] ); - atari_ntsc_out_t* restrict line_out = static_cast(rgb_out); - ++line_in; - - for ( uInt32 n = chunk_count; n; --n ) - { - /* order of input and output pixels must not be altered */ - ATARI_NTSC_COLOR_IN( 0, ntsc, line_in[0] ); - ATARI_NTSC_RGB_OUT_8888( 0, line_out[0] ); - ATARI_NTSC_RGB_OUT_8888( 1, line_out[1] ); - ATARI_NTSC_RGB_OUT_8888( 2, line_out[2] ); - ATARI_NTSC_RGB_OUT_8888( 3, line_out[3] ); - - ATARI_NTSC_COLOR_IN( 1, ntsc, line_in[1] ); - ATARI_NTSC_RGB_OUT_8888( 4, line_out[4] ); - ATARI_NTSC_RGB_OUT_8888( 5, line_out[5] ); - ATARI_NTSC_RGB_OUT_8888( 6, line_out[6] ); - - line_in += 2; - line_out += 7; - } - - /* finish final pixels */ - ATARI_NTSC_COLOR_IN( 0, ntsc, atari_ntsc_black ); - ATARI_NTSC_RGB_OUT_8888( 0, line_out[0] ); - ATARI_NTSC_RGB_OUT_8888( 1, line_out[1] ); - ATARI_NTSC_RGB_OUT_8888( 2, line_out[2] ); - ATARI_NTSC_RGB_OUT_8888( 3, line_out[3] ); - - ATARI_NTSC_COLOR_IN( 1, ntsc, atari_ntsc_black ); - ATARI_NTSC_RGB_OUT_8888( 4, line_out[4] ); - ATARI_NTSC_RGB_OUT_8888( 5, line_out[5] ); - ATARI_NTSC_RGB_OUT_8888( 6, line_out[6] ); - - atari_in += in_width; - rgb_out = static_cast(rgb_out) + out_pitch; - } -} - -void atari_ntsc_blit_double( atari_ntsc_t const* ntsc, - atari_ntsc_in_t const* atari_in1, atari_ntsc_in_t const* atari_in2, - uInt32 in_width, uInt32 in_height, void* rgb_out, uInt32 out_pitch ) -{ - #define TO_DOUBLE(pixel1, pixel2) (((pixel1>>1)<<7)+(pixel2>>1)) - - uInt32 const chunk_count = (in_width - 1) / atari_ntsc_in_chunk; - while ( in_height-- ) - { - atari_ntsc_in_t const* line_in1 = atari_in1; - atari_ntsc_in_t const* line_in2 = atari_in2; - ATARI_NTSC_BEGIN_ROW( ntsc, - TO_DOUBLE(atari_ntsc_black, atari_ntsc_black), - TO_DOUBLE(line_in1[0], line_in2[0]) ); - atari_ntsc_out_t* restrict line_out = static_cast(rgb_out); - ++line_in1; - ++line_in2; - - for ( uInt32 n = chunk_count; n; --n ) - { - /* order of input and output pixels must not be altered */ - ATARI_NTSC_COLOR_IN( 0, ntsc, - TO_DOUBLE(line_in1[0], line_in2[0]) ); - ATARI_NTSC_RGB_OUT_8888( 0, line_out[0] ); - ATARI_NTSC_RGB_OUT_8888( 1, line_out[1] ); - ATARI_NTSC_RGB_OUT_8888( 2, line_out[2] ); - ATARI_NTSC_RGB_OUT_8888( 3, line_out[3] ); - - ATARI_NTSC_COLOR_IN( 1, ntsc, - TO_DOUBLE(line_in1[1], line_in2[1]) ); - ATARI_NTSC_RGB_OUT_8888( 4, line_out[4] ); - ATARI_NTSC_RGB_OUT_8888( 5, line_out[5] ); - ATARI_NTSC_RGB_OUT_8888( 6, line_out[6] ); - - line_in1 += 2; - line_in2 += 2; - line_out += 7; - } - - /* finish final pixels */ - ATARI_NTSC_COLOR_IN( 0, ntsc, - TO_DOUBLE(atari_ntsc_black, atari_ntsc_black) ); - ATARI_NTSC_RGB_OUT_8888( 0, line_out[0] ); - ATARI_NTSC_RGB_OUT_8888( 1, line_out[1] ); - ATARI_NTSC_RGB_OUT_8888( 2, line_out[2] ); - ATARI_NTSC_RGB_OUT_8888( 3, line_out[3] ); - - ATARI_NTSC_COLOR_IN( 1, ntsc, - TO_DOUBLE(atari_ntsc_black, atari_ntsc_black) ); - ATARI_NTSC_RGB_OUT_8888( 4, line_out[4] ); - ATARI_NTSC_RGB_OUT_8888( 5, line_out[5] ); - ATARI_NTSC_RGB_OUT_8888( 6, line_out[6] ); - - atari_in1 += in_width; - atari_in2 += in_width; - rgb_out = static_cast(rgb_out) + out_pitch; - } -} diff --git a/src/common/tv_filters/atari_ntsc.hxx b/src/common/tv_filters/atari_ntsc.hxx deleted file mode 100644 index a331bc72c..000000000 --- a/src/common/tv_filters/atari_ntsc.hxx +++ /dev/null @@ -1,157 +0,0 @@ -//============================================================================ -// -// 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-2017 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. -//============================================================================ - -/* Atari TIA, CTIA, GTIA and MARIA NTSC video filter */ - -#ifndef ATARI_NTSC_H -#define ATARI_NTSC_H - -#include "bspf.hxx" - -using atari_ntsc_in_t = uInt8; -using atari_ntsc_out_t = uInt32; - -#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. */ -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) */ - float const* decoder_matrix; /* optional RGB decoder matrix, 6 elements */ -}; - -/* 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_bad; /* badly adjusted TV */ - -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, - atari_ntsc_in_t const* palette ); - -/* Filters one or more rows of pixels. Input pixels are 8-bit Atari palette colors. - 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. */ -void atari_ntsc_blit_single( atari_ntsc_t const* ntsc, - atari_ntsc_in_t const* atari_in, uInt32 in_width, uInt32 in_height, - void* rgb_out, uInt32 out_pitch ); -void atari_ntsc_blit_double( atari_ntsc_t const* ntsc, - atari_ntsc_in_t const* atari_in1, atari_ntsc_in_t const* atari_in2, - uInt32 in_width, uInt32 in_height, void* rgb_out, uInt32 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 160 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 = 2 }; /* 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 */ - -/* Begins outputting row and starts two pixels. First pixel will be cut off a bit. - Use atari_ntsc_black for unused pixels. Declares variables, so must be before first - statement in a block (unless you're using C++). */ -#define ATARI_NTSC_BEGIN_ROW( ntsc, pixel0, pixel1 ) \ - ATARI_NTSC_BEGIN_ROW_6_( pixel0, pixel1, ATARI_NTSC_ENTRY_, ntsc ) - -/* Begins input pixel */ -#define ATARI_NTSC_COLOR_IN( in_index, ntsc, color_in ) \ - ATARI_NTSC_COLOR_IN_( in_index, color_in, ATARI_NTSC_ENTRY_, ntsc ) - -/* Generates output in the specified 32-bit format (x = junk bits). - native: xxxRRRRR RRRxxGGG GGGGGxxB BBBBBBBx (native internal format) - 8888: 00000000 RRRRRRRR GGGGGGGG BBBBBBBB (8-8-8-8 32-bit ARGB) -*/ -#define ATARI_NTSC_RGB_OUT_8888( index, rgb_out ) {\ - atari_ntsc_rgb_t raw_ =\ - kernel0 [index ] + kernel1 [(index+10)%7+14] +\ - kernelx0 [(index+7)%14] + kernelx1 [(index+ 3)%7+14+7];\ - ATARI_NTSC_CLAMP_( raw_, 0 );\ - rgb_out = (raw_>>5 & 0x00FF0000)|(raw_>>3 & 0x0000FF00)|(raw_>>1 & 0x000000FF);\ -} - -/* private */ -enum { atari_ntsc_entry_size = 2 * 14 }; -using atari_ntsc_rgb_t = uInt32; -struct atari_ntsc_t { - atari_ntsc_rgb_t table [atari_ntsc_palette_size] [atari_ntsc_entry_size]; -}; - -#define ATARI_NTSC_ENTRY_( ntsc, n ) (ntsc)->table [n] - -/* common 3->7 ntsc macros */ -#define ATARI_NTSC_BEGIN_ROW_6_( pixel0, pixel1, 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_ );\ - atari_ntsc_rgb_t const* kernelx0;\ - atari_ntsc_rgb_t const* kernelx1 = kernel0 - -/* common ntsc macros */ -#define atari_ntsc_rgb_builder ((1 << 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_ ));\ -} - -#ifdef __cplusplus - } -#endif - -#endif diff --git a/src/common/tv_filters/atari_ntsc_impl.hxx b/src/common/tv_filters/atari_ntsc_impl.hxx deleted file mode 100644 index cf44e773b..000000000 --- a/src/common/tv_filters/atari_ntsc_impl.hxx +++ /dev/null @@ -1,432 +0,0 @@ -//============================================================================ -// -// 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-2017 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. -//============================================================================ - -/* Based on nes_ntsc 0.2.2. http://www.slack.net/~ant/ */ - -/* Common implementation of NTSC filters */ - -#include - -/* Copyright (C) 2006-2009 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 */ - -#ifndef DISABLE_CORRECTION - #define DISABLE_CORRECTION 0 -#endif - -#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) * 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 > 1.056 || pow_a_n < 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 - 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; - } - } - - /* 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; - } - } - } - - /* - 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 -} - -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; - - 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 */ - { - float hue = float(setup->hue) * PI + PI / 180 * ext_decoder_hue; - 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; // FIXME: dead code detected by llvm scan-build - do - { - float const* in = decoder; - int n2 = 3; - do - { - float i = *in++; - float q = *in++; - *out++ = i * c - q * s; - *out++ = i * s + q * c; - } - while ( --n2 ); - if ( burst_count <= 1 ) - break; - ROTATE_IQ( s, c, 0.866025f, -0.5f ); /* +120 degrees */ - } - while ( --n ); - } - } -} - -/* kernel generation */ - -#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)\ -) - -#ifndef PACK_RGB - #define PACK_RGB( r, g, b ) ((r) << 21 | (g) << 11 | (b) << 1) -#endif - -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 fi = k[0]*ic0 + k[2]*ic2; - float fq = k[1]*qc1 + k[3]*qc3; - float fy = 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( fy, fi, fq, 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 ); - -#if DISABLE_CORRECTION - #define CORRECT_ERROR( a ) { out [i] += rgb_bias; } - #define DISTRIBUTE_ERROR( a, b, c ) { out [i] += rgb_bias; } -#else - #define CORRECT_ERROR( a ) { out [a] += error; } - #define DISTRIBUTE_ERROR( a, b, c ) {\ - 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 [i] += error - (fourth * 3);\ - } -#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 diff --git a/src/common/tv_filters/module.mk b/src/common/tv_filters/module.mk index 96c89b7a1..02fedb50b 100644 --- a/src/common/tv_filters/module.mk +++ b/src/common/tv_filters/module.mk @@ -2,10 +2,10 @@ MODULE := src/common/tv_filters MODULE_OBJS := \ src/common/tv_filters/NTSCFilter.o \ - src/common/tv_filters/atari_ntsc.o + src/common/tv_filters/AtariNTSC.o MODULE_DIRS += \ src/common/tv_filters -# Include common rules +# Include common rules include $(srcdir)/common.rules diff --git a/src/emucore/TIASurface.cxx b/src/emucore/TIASurface.cxx index be95c9d2a..6c9efbde9 100644 --- a/src/emucore/TIASurface.cxx +++ b/src/emucore/TIASurface.cxx @@ -40,7 +40,7 @@ TIASurface::TIASurface(OSystem& system) myNTSCFilter.loadConfig(myOSystem.settings()); // Create a surface for the TIA image and scanlines; we'll need them eventually - myTiaSurface = myFB.allocateSurface(ATARI_NTSC_OUT_WIDTH(kTIAW), kTIAH); + myTiaSurface = myFB.allocateSurface(AtariNTSC::outWidth(kTIAW), kTIAH); // Generate scanline data, and a pre-defined scanline surface uInt32 scanData[kScanH]; @@ -258,7 +258,7 @@ void TIASurface::enableNTSC(bool enable) myFilterType = FilterType(enable ? myFilterType | 0x10 : myFilterType & 0x01); // Normal vs NTSC mode uses different source widths - myTiaSurface->setSrcSize(enable ? ATARI_NTSC_OUT_WIDTH(160) : 160, myTIA->height()); + myTiaSurface->setSrcSize(enable ? AtariNTSC::outWidth(160) : 160, myTIA->height()); FBSurface::Attributes& tia_attr = myTiaSurface->attributes(); tia_attr.smoothing = myOSystem.settings().getBool("tia.inter");