OK, Blargg is now officially in the codebase! There's still quite a bit

left to do, but the basic functionality is now there.  Alt 1 - Alt 6
selects among TV modes as follows: off, composite, svideo, rgb, bad and
custom.  The latter is just composite at this point; there's no code to
actually change specific adjustables yet.  Alt 7/Shift-Alt 7 increases/
decreases scanline blend intensity (20% - 100%).


git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@2456 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
This commit is contained in:
stephena 2012-05-06 17:45:48 +00:00
parent 1e8f45a10f
commit e3fa8da1f9
17 changed files with 473 additions and 555 deletions

View File

@ -27,24 +27,21 @@
#include "FBSurfaceTIA.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FBSurfaceTIA::FBSurfaceTIA(FrameBufferGL& buffer, uInt32 baseWidth, uInt32 baseHeight,
uInt32 imgX, uInt32 imgY, uInt32 imgW, uInt32 imgH)
FBSurfaceTIA::FBSurfaceTIA(FrameBufferGL& buffer)
: myFB(buffer),
myGL(myFB.p_gl),
myTexture(NULL),
myVBOID(0),
myImageX(imgX),
myImageY(imgY),
myImageW(imgW),
myImageH(imgH)
myScanlinesEnabled(false),
myScanlineIntensityI(50),
myScanlineIntensityF(0.5)
{
myTexID[0] = myTexID[1] = 0;
// Fill buffer struct with valid data
myTexWidth = FrameBufferGL::power_of_two(baseWidth);
myTexHeight = FrameBufferGL::power_of_two(baseHeight);
myTexCoordW = (GLfloat) baseWidth / myTexWidth;
myTexCoordH = (GLfloat) baseHeight / myTexHeight;
// Texture width is set to contain all possible sizes for a TIA image,
// including Blargg filtering
myTexWidth = FrameBufferGL::power_of_two(ATARI_NTSC_OUT_WIDTH(160));
myTexHeight = FrameBufferGL::power_of_two(320);
// Based on experimentation, the following are the fastest 16-bit
// formats for OpenGL (on all platforms)
@ -56,10 +53,6 @@ FBSurfaceTIA::FBSurfaceTIA(FrameBufferGL& buffer, uInt32 baseWidth, uInt32 baseH
0x0000f800, 0x000007c0, 0x0000003e, 0x00000000);
#endif
myPitch = myTexture->pitch >> 1;
// Associate the SDL surface with a GL texture object
updateCoords();
reload();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -98,6 +91,8 @@ void FBSurfaceTIA::update()
uInt32 height = myTIA->height();
uInt16* buffer = (uInt16*) myTexture->pixels;
// TODO - Eventually 'phosphor' won't be a separate mode, and will become
// a post-processing filter by blending several frames.
switch(myFB.myFilterType)
{
case FrameBufferGL::kNone:
@ -137,6 +132,14 @@ void FBSurfaceTIA::update()
case FrameBufferGL::kBlarggNTSC:
{
#ifdef HAVE_GL_BGRA
#define BLIT16 blit_1555
#else
#define BLIT16 blit_5551
#endif
myFB.myNTSCFilter.BLIT16(currentFrame, width,
myTexture->w, height,
buffer, myTexture->pitch);
break;
}
}
@ -163,11 +166,10 @@ void FBSurfaceTIA::update()
myGL.TexCoordPointer(2, GL_FLOAT, 0, (const GLvoid*)(8*sizeof(GLfloat)));
myGL.DrawArrays(GL_TRIANGLE_STRIP, 0, 4);
#if 0
if(1)//myFB.myScanlinesEnabled)
if(myScanlinesEnabled)
{
myGL.Enable(GL_BLEND);
myGL.Color4f(1.0f, 1.0f, 1.0f, 0.5f);
myGL.Color4f(1.0f, 1.0f, 1.0f, myScanlineIntensityF);
myGL.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
myGL.BindTexture(GL_TEXTURE_2D, myTexID[1]);
myGL.VertexPointer(2, GL_FLOAT, 0, (const GLvoid*)(16*sizeof(GLfloat)));
@ -175,7 +177,6 @@ void FBSurfaceTIA::update()
myGL.DrawArrays(GL_TRIANGLE_STRIP, 0, 4);
myGL.Disable(GL_BLEND);
}
#endif
}
else
{
@ -183,11 +184,10 @@ void FBSurfaceTIA::update()
myGL.TexCoordPointer(2, GL_FLOAT, 0, myCoord+8);
myGL.DrawArrays(GL_TRIANGLE_STRIP, 0, 4);
#if 0
if(1)//myFB.myScanlinesEnabled)
if(myScanlinesEnabled)
{
myGL.Enable(GL_BLEND);
myGL.Color4f(1.0f, 1.0f, 1.0f, 0.5f);
myGL.Color4f(1.0f, 1.0f, 1.0f, myScanlineIntensityF);
myGL.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
myGL.BindTexture(GL_TEXTURE_2D, myTexID[1]);
myGL.VertexPointer(2, GL_FLOAT, 0, myCoord+16);
@ -195,7 +195,6 @@ void FBSurfaceTIA::update()
myGL.DrawArrays(GL_TRIANGLE_STRIP, 0, 4);
myGL.Disable(GL_BLEND);
}
#endif
}
myGL.DisableClientState(GL_VERTEX_ARRAY);
@ -279,9 +278,7 @@ void FBSurfaceTIA::reload()
void FBSurfaceTIA::setFilter(const string& name)
{
// We only do GL_NEAREST or GL_LINEAR for now
GLint filter = GL_NEAREST;
if(name == "linear")
filter = GL_LINEAR;
GLint filter = name == "linear" ? GL_LINEAR : GL_NEAREST;
myGL.BindTexture(GL_TEXTURE_2D, myTexID[0]);
myGL.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter);
@ -290,9 +287,28 @@ void FBSurfaceTIA::setFilter(const string& name)
myGL.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceTIA::updateCoords(uInt32 baseH,
uInt32 imgX, uInt32 imgY, uInt32 imgW, uInt32 imgH)
{
myBaseH = baseH;
myImageX = imgX; myImageY = imgY;
myImageW = imgW; myImageH = imgH;
updateCoords();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceTIA::updateCoords()
{
// Normal TIA rendering and TV effects use different widths
// We use the same buffer, and only pick the width we need
myBaseW = myFB.myFilterType == FrameBufferGL::kBlarggNTSC ?
ATARI_NTSC_OUT_WIDTH(160) : 160;
myTexCoordW = (GLfloat) myBaseW / myTexWidth;
myTexCoordH = (GLfloat) myBaseH / myTexHeight;
// Vertex coordinates for texture 0 (main texture)
// Upper left (x,y)
myCoord[0] = (GLfloat)myImageX;
@ -344,10 +360,10 @@ void FBSurfaceTIA::updateCoords()
myCoord[27] = 0.0f;
// Lower left (x,y+h)
myCoord[28] = 0.0f;
myCoord[29] = (GLfloat)(myImageH/myFB.myZoomLevel);
myCoord[29] = GLfloat(myImageH) / (myImageH / myBaseH);
// Lower right (x+w,y+h)
myCoord[30] = 1.0f;
myCoord[31] = (GLfloat)(myImageH/myFB.myZoomLevel);
myCoord[31] = GLfloat(myImageH) / (myImageH / myBaseH);
// Cache vertex and texture coordinates using vertex buffer object
if(myFB.myVBOAvailable)
@ -360,7 +376,7 @@ void FBSurfaceTIA::updateCoords()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceTIA::setTIAPalette(const uInt32* palette)
{
myNTSCFilter.setTIAPalette(palette);
myFB.myNTSCFilter.setTIAPalette(palette);
}
#endif

View File

@ -25,7 +25,6 @@
#include "bspf.hxx"
#include "FrameBuffer.hxx"
#include "FrameBufferGL.hxx"
#include "NTSCFilter.hxx"
/**
A surface suitable for OpenGL rendering mode, but specifically for
@ -40,8 +39,7 @@ class FBSurfaceTIA : public FBSurface
friend class FrameBufferGL;
public:
FBSurfaceTIA(FrameBufferGL& buffer, uInt32 baseWidth, uInt32 baseHeight,
uInt32 imgX, uInt32 imgY, uInt32 imgW, uInt32 imgH);
FBSurfaceTIA(FrameBufferGL& buffer);
virtual ~FBSurfaceTIA();
// TIA surfaces don't implement most of the drawing primitives,
@ -59,6 +57,8 @@ class FBSurfaceTIA : public FBSurface
void setTIA(const TIA& tia) { myTIA = &tia; }
void setTIAPalette(const uInt32* palette);
void setFilter(const string& name);
void enableScanlines(bool enable) { myScanlinesEnabled = enable; }
void updateCoords(uInt32 baseH, uInt32 imgX, uInt32 imgY, uInt32 imgW, uInt32 imgH);
void updateCoords();
private:
@ -66,15 +66,19 @@ class FBSurfaceTIA : public FBSurface
const FrameBufferGL::GLpointers& myGL;
const TIA* myTIA;
SDL_Surface* myTexture;
NTSCFilter myNTSCFilter;
uInt32 myPitch;
GLuint myTexID[2], myVBOID;
GLsizei myTexWidth;
GLsizei myTexHeight;
GLuint myBaseW, myBaseH;
GLuint myImageX, myImageY, myImageW, myImageH;
GLfloat myTexCoordW, myTexCoordH;
GLfloat myCoord[32];
bool myScanlinesEnabled;
GLuint myScanlineIntensityI;
GLfloat myScanlineIntensityF;
};
#endif // DISPLAY_OPENGL

View File

@ -200,19 +200,16 @@ string FrameBufferGL::about() const
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool FrameBufferGL::setVidMode(VideoMode& mode)
{
bool inUIMode =
myOSystem->eventHandler().state() == EventHandler::S_LAUNCHER ||
myOSystem->eventHandler().state() == EventHandler::S_DEBUGGER;
bool inTIAMode =
myOSystem->eventHandler().state() != EventHandler::S_LAUNCHER &&
myOSystem->eventHandler().state() != EventHandler::S_DEBUGGER;
// Grab the initial width and height before it's updated below
uInt32 baseWidth = mode.image_w / mode.gfxmode.zoom;
// Grab the initial height before it's updated below
// We need it for the creating the TIA surface
uInt32 baseHeight = mode.image_h / mode.gfxmode.zoom;
// Set the zoom level
myZoomLevel = mode.gfxmode.zoom;
// Aspect ratio and fullscreen stretching only applies to the TIA
if(!inUIMode)
if(inTIAMode)
{
// Aspect ratio (depends on whether NTSC or PAL is detected)
// Not available in 'small' resolutions
@ -225,18 +222,36 @@ bool FrameBufferGL::setVidMode(VideoMode& mode)
}
// Fullscreen mode stretching
if(fullScreen() && myOSystem->settings().getBool("gl_fsmax") &&
if(fullScreen() &&
(mode.image_w < mode.screen_w) && (mode.image_h < mode.screen_h))
{
float stretchFactor = 1.0;
float scaleX = float(mode.image_w) / mode.screen_w;
float scaleY = float(mode.image_h) / mode.screen_h;
if(scaleX > scaleY)
stretchFactor = float(mode.screen_w) / mode.image_w;
// Scale to actual or integral factors
if(myOSystem->settings().getBool("gl_fsscale"))
{
// Scale to full (non-integral) available space
if(scaleX > scaleY)
stretchFactor = float(mode.screen_w) / mode.image_w;
else
stretchFactor = float(mode.screen_h) / mode.image_h;
}
else
stretchFactor = float(mode.screen_h) / mode.image_h;
{
// Only scale to an integral amount
if(scaleX > scaleY)
{
int bw = mode.image_w / mode.gfxmode.zoom;
stretchFactor = float(int(mode.screen_w / bw) * bw) / mode.image_w;
}
else
{
int bh = mode.image_h / mode.gfxmode.zoom;
stretchFactor = float(int(mode.screen_h / bh) * bh) / mode.image_h;
}
}
mode.image_w = (Uint16) (stretchFactor * mode.image_w);
mode.image_h = (Uint16) (stretchFactor * mode.image_h);
}
@ -296,62 +311,34 @@ bool FrameBufferGL::setVidMode(VideoMode& mode)
p_gl.MatrixMode(GL_MODELVIEW);
p_gl.LoadIdentity();
/*
#if 0
cerr << "dimensions: " << (fullScreen() ? "(full)" : "") << endl
<< " screen w = " << mode.screen_w << endl
<< " screen h = " << mode.screen_h << endl
<< " image x = " << mode.image_x << endl
<< " image y = " << mode.image_y << endl
<< " image w = " << mode.image_w << endl
<< " image h = " << mode.image_h << endl
<< " base w = " << baseWidth << endl
<< " base h = " << baseHeight << endl
<< endl;
*/
<< mode << endl;
#endif
////////////////////////////////////////////////////////////////////
// Note that the following must be done in the order given
// Basically, all surfaces must first be free'd before being
// recreated
// So, we delete the TIA surface first, then reset all other surfaces
// (which frees all surfaces and then reloads all surfaces), then
// re-create the TIA surface (if necessary)
// In this way, all free()'s come before all reload()'s
////////////////////////////////////////////////////////////////////
// We try to re-use the TIA surface whenever possible
if(!inUIMode && !(myTiaSurface &&
myTiaSurface->getWidth() == mode.image_w &&
myTiaSurface->getHeight() == mode.image_h))
// The framebuffer only takes responsibility for TIA surfaces
// Other surfaces (such as the ones used for dialogs) are allocated
// in the Dialog class
if(inTIAMode)
{
delete myTiaSurface; myTiaSurface = NULL;
// Since we have free hardware stretching, the base TIA surface is created
// only once, and its texture coordinates changed when we want to draw a
// smaller or larger image
if(!myTiaSurface)
myTiaSurface = new FBSurfaceTIA(*this);
myTiaSurface->updateCoords(baseHeight, mode.image_x, mode.image_y,
mode.image_w, mode.image_h);
myTiaSurface->setFilter(myOSystem->settings().getString("gl_filter"));
myTiaSurface->enableScanlines(myFilterType == kBlarggNTSC);
myTiaSurface->setTIA(myOSystem->console().tia());
}
// Any previously allocated textures currently in use by various UI items
// need to be refreshed as well (only seems to be required for OSX)
resetSurfaces(myTiaSurface);
// The framebuffer only takes responsibility for TIA surfaces
// Other surfaces (such as the ones used for dialogs) are allocated
// in the Dialog class
if(!inUIMode)
{
// The actual TIA image is only half of that specified by baseWidth
// The stretching can be done in hardware now that the TIA surface
// and other UI surfaces are no longer tied together
// Note that this may change in the future, when we add more
// complex filters/scalers, but for now it's fine
//
// Also note that TV filtering is always available since we'll always
// have access to Blargg filtering
if(!myTiaSurface)
myTiaSurface = new FBSurfaceTIA(*this, baseWidth>>1, baseHeight,
mode.image_x, mode.image_y, mode.image_w, mode.image_h);
myTiaSurface->setFilter(myOSystem->settings().getString("gl_filter"));
myTiaSurface->setTIA(myOSystem->console().tia());
}
return true;
}
@ -392,12 +379,32 @@ void FrameBufferGL::enablePhosphor(bool enable, int blend)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBufferGL::enableNTSC(bool enable)
{
if(enable)
myFilterType = kBlarggNTSC;
else
myFilterType = myUsePhosphor ? kPhosphor : kNone;
if(myTiaSurface)
{
myFilterType = enable ? kBlarggNTSC : myUsePhosphor ? kPhosphor : kNone;
myTiaSurface->updateCoords();
myTiaSurface->enableScanlines(myFilterType == kBlarggNTSC);
myRedrawEntireFrame = true;
}
}
myRedrawEntireFrame = true;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 FrameBufferGL::changeScanlines(int relative, int absolute)
{
uInt32 intensity = myTiaSurface->myScanlineIntensityI;
if(myTiaSurface)
{
if(relative == 0) intensity = absolute;
else intensity += relative;
intensity = BSPF_max(20u, intensity);
intensity = BSPF_min(100u, intensity);
myTiaSurface->myScanlineIntensityI = (GLuint)intensity;
myTiaSurface->myScanlineIntensityF = (GLfloat)intensity / 100;
myRedrawEntireFrame = true;
}
return intensity;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -91,6 +91,20 @@ class FrameBufferGL : public FrameBuffer
Enable/disable NTSC filtering effects.
*/
void enableNTSC(bool enable);
bool ntscEnabled() const { return myFilterType == kBlarggNTSC; }
/**
Change scanline intensity.
relative = -1 means decrease current intensity by 'directin
direction = 0 means to reload the current video mode
direction = +1 means go to the next higher video mode
@param relative If non-zero, change current intensity by
'relative' amount, otherwise set to 'absolute'
@return New current intensity
*/
uInt32 changeScanlines(int relative, int absolute = 50);
/**
Set up the TIA/emulation palette for a screen of any depth > 8.

View File

@ -34,6 +34,7 @@
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FrameBufferSoft::FrameBufferSoft(OSystem* osystem)
: FrameBuffer(osystem),
myZoomLevel(2),
myRenderType(kSoftZoom_16),
myTiaDirty(false),
myInUIMode(false),

View File

@ -58,11 +58,6 @@ class FrameBufferSoft : public FrameBuffer
*/
void enablePhosphor(bool enable, int blend);
/**
Enable/disable NTSC filtering effects (not supported in software mode).
*/
void enableNTSC(bool enable) { }
/**
This method is called to retrieve the R/G/B data from the given pixel.
@ -155,6 +150,7 @@ class FrameBufferSoft : public FrameBuffer
string about() const;
private:
int myZoomLevel;
int myBytesPerPixel;
int myBaseOffset;
int myPitch;

View File

@ -17,20 +17,26 @@
// $Id$
//============================================================================
#include <cstring>
#include <cmath>
#include <cstdlib>
#include "NTSCFilter.hxx"
#ifndef M_PI
#define M_PI 3.141592653589793
#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
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NTSCFilter::NTSCFilter()
: mySetup(atari_ntsc_composite),
myCurrentModeNum(0)
myCustomSetup(atari_ntsc_composite)
{
}
@ -55,258 +61,52 @@ void NTSCFilter::setTIAPalette(const uInt32* palette)
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void NTSCFilter::updateFilter()
string NTSCFilter::setPreset(Preset preset)
{
double yiq_table[384];
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::restoreDefaults()
{
mySetup = atari_ntsc_composite;
string msg = "disabled";
switch(preset)
{
case PRESET_COMPOSITE:
mySetup = atari_ntsc_composite;
msg = "COMPOSITE";
break;
case PRESET_SVIDEO:
mySetup = atari_ntsc_svideo;
msg = "S-VIDEO";
break;
case PRESET_RGB:
mySetup = atari_ntsc_rgb;
msg = "RGB";
break;
case PRESET_BAD:
mySetup = atari_ntsc_bad;
msg = "BAD";
break;
case PRESET_CUSTOM:
mySetup = myCustomSetup;
msg = "CUSTOM";
break;
default:
return msg;
}
updateFilter();
return msg;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void NTSCFilter::setPreset(int preset)
{
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
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int NTSCFilter::getPreset()
{
// FIXME - for now just return composite
return 0;
#if 0
int i;
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;
}
return PRESET_CUSTOM;
#endif
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void NTSCFilter::nextPreset()
void NTSCFilter::updateAdjustables(const atari_ntsc_setup_t& setup)
{
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::FILTER_NTSC_WriteConfig(FILE *fp)
{
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);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int NTSCFilter::FILTER_NTSC_Initialise(int *argc, char *argv[])
{
int i;
int j;
for (i = j = 1; i < *argc; 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-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;
myAdjustables.hue = setup.hue;
myAdjustables.saturation = setup.saturation;
myAdjustables.contrast = setup.contrast;
myAdjustables.brightness = setup.brightness;
myAdjustables.sharpness = setup.sharpness;
myAdjustables.gamma = setup.gamma;
myAdjustables.resolution = setup.resolution;
myAdjustables.artifacts = setup.artifacts;
myAdjustables.fringing = setup.fringing;
myAdjustables.bleed = setup.bleed;
myAdjustables.burst_phase = setup.burst_phase;
}
#endif
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void NTSCFilter::updateYIQTable(double yiq_table[384], double start_angle)
{
// FIXME - const double start_saturation = 0.0; // calculated internally
// FIXME - const double gamma = 1; // 1 - COLOURS_NTSC_setup.gamma / 2.0;
uInt8* ext_ptr = myTIAPalette;
int n;
start_angle = - ((213.0f) * M_PI / 180.0f) - start_angle;
for (n = 0; n < 128; 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,
&atari_ntsc_bad,
&atari_ntsc_horrible
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static char const * const preset_cfg_strings[NTSCFilter::PRESET_SIZE] = {
"COMPOSITE",
"SVIDEO",
"RGB",
"MONOCHROME",
"BAD",
"HORRIBLE"
};

View File

@ -22,29 +22,17 @@
#include "bspf.hxx"
#include "Array.hxx"
#include "Settings.hxx"
#include "atari_ntsc.h"
// 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 Atari800,
and is derived from 'filter_ntsc.(h|c)'.
and is derived from 'filter_ntsc.(h|c)'. Original code based on
implementation from http://www.slack.net/~ant.
Original code based on implementation from http://www.slack.net/~ant.
Atari TIA NTSC composite video to RGB emulator/blitter.
The class is basically a thin wrapper around atari_ntsc_xxx structs
and methods, so that the rest of the codebase isn't affected by
updated versions of Blargg code.
*/
class NTSCFilter
{
@ -52,18 +40,23 @@ class NTSCFilter
NTSCFilter();
virtual ~NTSCFilter();
/* Set/get one of the available preset adjustments: Composite, S-Video, RGB,
Monochrome. */
enum {
public:
// Set one of the available preset adjustments (Composite, S-Video, RGB, etc)
enum Preset {
PRESET_OFF,
PRESET_COMPOSITE,
PRESET_SVIDEO,
PRESET_RGB,
PRESET_MONOCHROME,
PRESET_BAD,
PRESET_HORRIBLE,
PRESET_CUSTOM,
/* Number of "normal" (not including CUSTOM) values in enumerator */
PRESET_SIZE = PRESET_CUSTOM
PRESET_CUSTOM
};
/* Normally used in conjunction with custom mode, contains all
aspects currently adjustable in NTSC TV emulation. */
struct Adjustable {
double hue, saturation, contrast, brightness, gamma,
sharpness, resolution, artifacts, fringing, bleed,
burst_phase;
};
public:
@ -73,49 +66,58 @@ class NTSCFilter
*/
void setTIAPalette(const uInt32* palette);
/* Restores default values for NTSC-filter-specific colour controls.
updateFilter should be called afterwards to apply changes. */
void restoreDefaults();
// The following are meant to be used strictly for toggling from the GUI
string setPreset(Preset preset);
/* updateFilter should be called afterwards these functions to apply changes. */
void setPreset(int preset);
int getPreset();
void nextPreset();
// Reinitialises the NTSC filter (automatically called after settings
// have changed)
void updateFilter()
{
mySetup.palette = myTIAPalette;
atari_ntsc_init(&myFilter, &mySetup);
}
#if 0 // FIXME
/* Read/write to configuration file. */
int FILTER_NTSC_ReadConfig(char *option, char *ptr);
void FILTER_NTSC_WriteConfig(FILE *fp);
// Load and save NTSC-related settings
void loadConfig(const Settings& settings);
void saveSettings(Settings& settings) const;
/* 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).
*/
void updateYIQTable(double yiq_table[768], double start_angle);
// Perform Blargg filtering on input buffer, place results in
// output buffer
void blit_5551(uInt8* src_buf, long src_row_width,
int src_width, int src_height,
uInt16* dest_buf, long dest_pitch)
{
atari_ntsc_blit_5551(&myFilter, src_buf, src_row_width,
src_width, src_height,
dest_buf, dest_pitch);
}
void blit_1555(uInt8* src_buf, long src_row_width,
int src_width, int src_height,
uInt16* dest_buf, long dest_pitch)
{
atari_ntsc_blit_1555(&myFilter, src_buf, src_row_width,
src_width, src_height,
dest_buf, dest_pitch);
}
private:
// Pointer to the NTSC filter structure
// The NTSC filter structure
atari_ntsc_t myFilter;
// 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;
uInt8 myTIAPalette[384]; // 128 colours by 3 components per colour
// This setup is used only in custom mode (after it is modified,
// it is copied to mySetup)
atari_ntsc_setup_t myCustomSetup;
int myCurrentModeNum;
Common::Array<atari_ntsc_setup_t> myModeList;
// Contains adjustable settings for the current preset
// (including the custom mode)
Adjustable myAdjustables;
static atari_ntsc_setup_t const * const presets[PRESET_SIZE];
static char const * const preset_cfg_strings[PRESET_SIZE];
// 128 colours by 3 components per colour
uInt8 myTIAPalette[128 * 3];
};
#endif

View File

@ -31,30 +31,25 @@
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_composite =
{ 0.0, 0.0, 0.0, 0.0 , -0.5, .3, -0.1 , 0.0, 0.0, 0.0, 0, 0, 0, 0. };
atari_ntsc_setup_t const atari_ntsc_svideo =
{ 0.0, 0.0, 0.0, 0.0 , -0.3, .3, 0.2 , -1.0, -1.0, 0.0, 0, 0, 0, 0. };
atari_ntsc_setup_t const atari_ntsc_rgb =
{ 0.0, 0.0, 0.0, 0.0 , -0.3, .3, 0.7 , -1.0, -1.0, -1.0, 0, 0, 0, 0. };
atari_ntsc_setup_t const atari_ntsc_monochrome =
{ 0.0, -1.0, 0.0, 0.0 , -0.3, .3, 0.2 , -0.2, -0.2, -1.0, 0, 0, 0, 0. };
atari_ntsc_setup_t const atari_ntsc_bad =
{ 0.1, -0.3, 0.3, 0.25, 0.2, 0, 0.1 , 0.5, 0.5, 0.5, 0, 0, 0, 0. };
atari_ntsc_setup_t const atari_ntsc_horrible =
{ -0.1, -0.5, 0.6, 0.43, 0.4, 0, 0.05, 0.7, -0.8, -0.7, 0, 0, 0, 0. };
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, 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, 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, 0, 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, 0, 0, 0 };
atari_ntsc_setup_t const atari_ntsc_horrible = { -0.1, -0.5, 0.6, 0.43, 0.4, 0.0, 0.05, 0.7, -0.8, -0.7, 0, 0, 0, 0 };
#define alignment_count 2
#define burst_count 1
#define rescale_in 8
#define rescale_out 7
#define artifacts_mid 1.0f
#define artifacts_mid 1.5f
#define artifacts_max 2.5f
#define fringing_mid 1.0f
#define std_decoder_hue 0
#define gamma_size 256
#define default_palette_contrast 1.0f
#include "atari_ntsc_impl.h"
/* 2 input pixels -> 8 composite samples */
@ -81,48 +76,43 @@ void atari_ntsc_init( atari_ntsc_t* ntsc, atari_ntsc_setup_t const* setup )
if ( !setup )
setup = &atari_ntsc_composite;
init( &impl, setup );
// Palette stores R/G/B data for 'atari_ntsc_palette_size' entries
atari_ntsc_in_t* palette = ( atari_ntsc_in_t*) setup->palette;
/* Atari change: no alternating burst phases - remove code for merge_fields. */
// Burst-phase (TODO - how is this actually used?)
// float start_angle = - ((213.0f) * M_PI / 180.0f) - setup->burst_phase * M_PI;
for (int entry = 0; entry < atari_ntsc_palette_size; entry++ )
{
double* yiq_ptr = setup->yiq_palette + 3 * entry;
double y = *yiq_ptr++;
double i = *yiq_ptr++;
double q = *yiq_ptr++;
float r = impl.to_float [*palette++];
float g = impl.to_float [*palette++];
float b = impl.to_float [*palette++];
i *= rgb_unit;
q *= rgb_unit;
y *= rgb_unit;
y += rgb_offset;
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 ); //(ib < 0x3E0 ? ib: 0x3E0)
/* 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 );
}
}
}
void atari_ntsc_blit_rgb16( atari_ntsc_t const* ntsc, ATARI_NTSC_IN_T const* atari_in,
void atari_ntsc_blit_5551( 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 )
{
int 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, ATARI_NTSC_ADJ_IN( *(line_in[0]) ) );
atari_ntsc_in_t const* line_in = atari_in;
ATARI_NTSC_BEGIN_ROW( ntsc, atari_ntsc_black, ATARI_NTSC_ADJ_IN( line_in[0] ) );
atari_ntsc_out_t* restrict line_out = (atari_ntsc_out_t*) rgb_out;
int n;
++line_in;
@ -130,16 +120,16 @@ void atari_ntsc_blit_rgb16( atari_ntsc_t const* ntsc, ATARI_NTSC_IN_T const* ata
for ( n = chunk_count; n; --n )
{
/* order of input and output pixels must not be altered */
ATARI_NTSC_COLOR_IN( 0, ntsc, ATARI_NTSC_ADJ_IN( *(line_in [0]) ) ); //CHANGED TO DEREFERENCE POINTER
ATARI_NTSC_RGB_OUT_RGB16( 0, line_out [0] );
ATARI_NTSC_RGB_OUT_RGB16( 1, line_out [1] );
ATARI_NTSC_RGB_OUT_RGB16( 2, line_out [2] );
ATARI_NTSC_RGB_OUT_RGB16( 3, line_out [3] );
ATARI_NTSC_COLOR_IN( 0, ntsc, ATARI_NTSC_ADJ_IN( line_in[0] ) );
ATARI_NTSC_RGB_OUT_5551( 0, line_out [0] );
ATARI_NTSC_RGB_OUT_5551( 1, line_out [1] );
ATARI_NTSC_RGB_OUT_5551( 2, line_out [2] );
ATARI_NTSC_RGB_OUT_5551( 3, line_out [3] );
ATARI_NTSC_COLOR_IN( 1, ntsc, ATARI_NTSC_ADJ_IN( *(line_in [1]) ) ); //CHANGED TO DEREFERENCE POINTER
ATARI_NTSC_RGB_OUT_RGB16( 4, line_out [4] );
ATARI_NTSC_RGB_OUT_RGB16( 5, line_out [5] );
ATARI_NTSC_RGB_OUT_RGB16( 6, line_out [6] );
ATARI_NTSC_COLOR_IN( 1, ntsc, ATARI_NTSC_ADJ_IN( line_in[1] ) );
ATARI_NTSC_RGB_OUT_5551( 4, line_out [4] );
ATARI_NTSC_RGB_OUT_5551( 5, line_out [5] );
ATARI_NTSC_RGB_OUT_5551( 6, line_out [6] );
line_in += 2;
line_out += 7;
@ -147,30 +137,30 @@ void atari_ntsc_blit_rgb16( atari_ntsc_t const* ntsc, ATARI_NTSC_IN_T const* ata
/* finish final pixels */
ATARI_NTSC_COLOR_IN( 0, ntsc, atari_ntsc_black );
ATARI_NTSC_RGB_OUT_RGB16( 0, line_out [0] );
ATARI_NTSC_RGB_OUT_RGB16( 1, line_out [1] );
ATARI_NTSC_RGB_OUT_RGB16( 2, line_out [2] );
ATARI_NTSC_RGB_OUT_RGB16( 3, line_out [3] );
ATARI_NTSC_RGB_OUT_5551( 0, line_out [0] );
ATARI_NTSC_RGB_OUT_5551( 1, line_out [1] );
ATARI_NTSC_RGB_OUT_5551( 2, line_out [2] );
ATARI_NTSC_RGB_OUT_5551( 3, line_out [3] );
ATARI_NTSC_COLOR_IN( 1, ntsc, atari_ntsc_black );
ATARI_NTSC_RGB_OUT_RGB16( 4, line_out [4] );
ATARI_NTSC_RGB_OUT_RGB16( 5, line_out [5] );
ATARI_NTSC_RGB_OUT_RGB16( 6, line_out [6] );
ATARI_NTSC_RGB_OUT_5551( 4, line_out [4] );
ATARI_NTSC_RGB_OUT_5551( 5, line_out [5] );
ATARI_NTSC_RGB_OUT_5551( 6, line_out [6] );
atari_in += 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* atari_in,
void atari_ntsc_blit_1555( 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 )
{
int 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, ATARI_NTSC_ADJ_IN( *(line_in[0]) ) );
atari_ntsc_in_t const* line_in = atari_in;
ATARI_NTSC_BEGIN_ROW( ntsc, atari_ntsc_black, ATARI_NTSC_ADJ_IN( line_in[0] ) );
atari_ntsc_out_t* restrict line_out = (atari_ntsc_out_t*) rgb_out;
int n;
++line_in;
@ -178,16 +168,16 @@ void atari_ntsc_blit_bgr16( atari_ntsc_t const* ntsc, ATARI_NTSC_IN_T const* ata
for ( n = chunk_count; n; --n )
{
/* order of input and output pixels must not be altered */
ATARI_NTSC_COLOR_IN( 0, ntsc, ATARI_NTSC_ADJ_IN( *(line_in [0]) ) ); //CHANGED TO DEREFERENCE POINTER
ATARI_NTSC_RGB_OUT_BGR16( 0, line_out [0] );
ATARI_NTSC_RGB_OUT_BGR16( 1, line_out [1] );
ATARI_NTSC_RGB_OUT_BGR16( 2, line_out [2] );
ATARI_NTSC_RGB_OUT_BGR16( 3, line_out [3] );
ATARI_NTSC_COLOR_IN( 0, ntsc, ATARI_NTSC_ADJ_IN( line_in[0] ) );
ATARI_NTSC_RGB_OUT_1555( 0, line_out [0] );
ATARI_NTSC_RGB_OUT_1555( 1, line_out [1] );
ATARI_NTSC_RGB_OUT_1555( 2, line_out [2] );
ATARI_NTSC_RGB_OUT_1555( 3, line_out [3] );
ATARI_NTSC_COLOR_IN( 1, ntsc, ATARI_NTSC_ADJ_IN( *(line_in [1]) ) ); //CHANGED TO DEREFERENCE POINTER
ATARI_NTSC_RGB_OUT_BGR16( 4, line_out [4] );
ATARI_NTSC_RGB_OUT_BGR16( 5, line_out [5] );
ATARI_NTSC_RGB_OUT_BGR16( 6, line_out [6] );
ATARI_NTSC_COLOR_IN( 1, ntsc, ATARI_NTSC_ADJ_IN( line_in[1] ) );
ATARI_NTSC_RGB_OUT_1555( 4, line_out [4] );
ATARI_NTSC_RGB_OUT_1555( 5, line_out [5] );
ATARI_NTSC_RGB_OUT_1555( 6, line_out [6] );
line_in += 2;
line_out += 7;
@ -195,15 +185,15 @@ void atari_ntsc_blit_bgr16( atari_ntsc_t const* ntsc, ATARI_NTSC_IN_T const* ata
/* finish final pixels */
ATARI_NTSC_COLOR_IN( 0, ntsc, atari_ntsc_black );
ATARI_NTSC_RGB_OUT_BGR16( 0, line_out [0] );
ATARI_NTSC_RGB_OUT_BGR16( 1, line_out [1] );
ATARI_NTSC_RGB_OUT_BGR16( 2, line_out [2] );
ATARI_NTSC_RGB_OUT_BGR16( 3, line_out [3] );
ATARI_NTSC_RGB_OUT_1555( 0, line_out [0] );
ATARI_NTSC_RGB_OUT_1555( 1, line_out [1] );
ATARI_NTSC_RGB_OUT_1555( 2, line_out [2] );
ATARI_NTSC_RGB_OUT_1555( 3, line_out [3] );
ATARI_NTSC_COLOR_IN( 1, ntsc, atari_ntsc_black );
ATARI_NTSC_RGB_OUT_BGR16( 4, line_out [4] );
ATARI_NTSC_RGB_OUT_BGR16( 5, line_out [5] );
ATARI_NTSC_RGB_OUT_BGR16( 6, line_out [6] );
ATARI_NTSC_RGB_OUT_1555( 4, line_out [4] );
ATARI_NTSC_RGB_OUT_1555( 5, line_out [5] );
ATARI_NTSC_RGB_OUT_1555( 6, line_out [6] );
atari_in += in_row_width;
rgb_out = (char*) rgb_out + out_pitch;

View File

@ -26,9 +26,9 @@
extern "C" {
#endif
/* Type of input pixel values. You'll probably use unsigned short
if you enable emphasis above. */
#define ATARI_NTSC_IN_T unsigned char *
/* Type of input and output pixel values. */
typedef unsigned char atari_ntsc_in_t;
typedef unsigned short atari_ntsc_out_t;
/* 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. */
@ -61,16 +61,13 @@ typedef struct atari_ntsc_setup_t
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_monochrome;/* desaturated + artifacts */
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;
extern atari_ntsc_setup_t const atari_ntsc_horrible; /* desaturated + artifacts */
enum { atari_ntsc_palette_size = 128 };
@ -81,12 +78,11 @@ void atari_ntsc_init( atari_ntsc_t* ntsc, atari_ntsc_setup_t const* setup );
/* 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. Output pixel format
is set by ATARI_NTSC_OUT_DEPTH (defaults to 16-bit RGB). */
void atari_ntsc_blit_rgb16( atari_ntsc_t const* ntsc, ATARI_NTSC_IN_T const* atari_in,
is the number of *bytes* to get to the next output row. */
void atari_ntsc_blit_5551( 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,
void atari_ntsc_blit_1555( 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 );
@ -105,9 +101,9 @@ value. */
/* 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 */
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
@ -119,20 +115,24 @@ enum { atari_ntsc_black = 0 }; /* palette index for black */
#define ATARI_NTSC_COLOR_IN( in_index, ntsc, color_in ) \
ATARI_NTSC_COLOR_IN_( in_index, color_in, ATARI_NTSC_ENTRY_, ntsc )
/* Generates output pixel. Bits can be RGB16 or BGR16 */
#define ATARI_NTSC_RGB_OUT_RGB16( index, rgb_out ) {\
/* Generates output in the specified 16-bit format (x = junk bits).
5551: RRRRRGGG GGBBBBBx (5-5-5-1 16-bit RGB)
1555: xRRRRRGG GGGBBBBB (1-5-5-5 16-bit RGB)
native: xxxRRRRR RRRxxGGG GGGGGxxB BBBBBBBx (native internal format)
*/
#define ATARI_NTSC_RGB_OUT_5551( 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_>>(13)& 0xF800)|(raw_>>(8)&0x07E0)|(raw_>>(4)&0x001F);\
rgb_out = (raw_>>(13)& 0xF800)|(raw_>>(8)&0x07C0)|(raw_>>(3)&0x003E);\
}
#define ATARI_NTSC_RGB_OUT_BGR16( index, rgb_out ) {\
#define ATARI_NTSC_RGB_OUT_1555( 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_>>(13)& 0xF800)|(raw_>>(8)&0x07E0)|(raw_>>(4)&0x001F);\
rgb_out = (raw_>>(14)& 0x7C00)|(raw_>>(9)&0x03E0)|(raw_>>(4)&0x001F);\
}
/* private */

View File

@ -24,8 +24,6 @@
#include <assert.h>
#include <math.h>
#include "bspf.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
@ -215,8 +213,6 @@ static void init_filters( init_t* impl, atari_ntsc_setup_t const* setup )
#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 };
@ -255,6 +251,7 @@ static void init( init_t* impl, atari_ntsc_setup_t const* setup )
/* setup decoder matricies */
{
#if 0 // FIXME - research this
/* 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
@ -265,6 +262,9 @@ static void init( init_t* impl, atari_ntsc_setup_t const* setup )
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;
#else
float hue = (float) setup->hue * PI + PI / 180 * ext_decoder_hue;
#endif
float sat = (float) setup->saturation + 1;
float const* decoder = setup->decoder_matrix;
if ( !decoder )
@ -304,14 +304,11 @@ static void init( init_t* impl, atari_ntsc_setup_t const* setup )
/* 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),\
@ -355,7 +352,6 @@ static void gen_kernel( init_t* impl, float y, float i, float q, atari_ntsc_rgb_
/* generate for each scanline burst phase */
float const* to_rgb = impl->to_rgb;
int burst_remain = burst_count;
y -= rgb_offset;
do
{
@ -418,21 +414,19 @@ static void gen_kernel( init_t* impl, float y, float i, float q, atari_ntsc_rgb_
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; }
#define DISTRIBUTE_ERROR( a, b, c ) { out [i] += rgb_bias; }
#else
#define CORRECT_ERROR( a ) { out [a] += error; }
#define DISTRIBUTE_ERROR( a, b, c, d ) {\
#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 [d] += fourth;\
out [i] += error - (fourth * 4);\
out [i] += error - (fourth * 3);\
}
#endif
@ -458,9 +452,3 @@ static void correct_errors( atari_ntsc_rgb_t color, atari_ntsc_rgb_t* out );
#define restrict
#endif
#endif
#if ATARI_NTSC_OUT_DEPTH <= 16
typedef uInt16 atari_ntsc_out_t;
#else
typedef uInt32 atari_ntsc_out_t;
#endif

View File

@ -444,21 +444,50 @@ void Console::togglePhosphor()
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Console::toggleNTSC()
void Console::toggleNTSC(NTSCFilter::Preset preset, bool show)
{
ostringstream buf;
if(myOSystem->frameBuffer().type() == kDoubleBuffer)
{
bool state = !myOSystem->settings().getBool("ntsc_filter");
myOSystem->frameBuffer().enableNTSC(state);
myOSystem->settings().setBool("ntsc_filter", state);
buf << "NTSC filtering " << (state ? "enabled" : "disabled");
myOSystem->frameBuffer().showMessage(buf.str());
if(preset == NTSCFilter::PRESET_OFF)
{
myOSystem->frameBuffer().enableNTSC(false);
buf << "TV filtering disabled";
}
else
{
myOSystem->frameBuffer().enableNTSC(true);
const string& mode = myOSystem->frameBuffer().ntsc().setPreset(preset);
buf << "TV filtering (" << mode << " mode)";
}
myOSystem->settings().setInt("tv_filter", (int)preset);
if(show) myOSystem->frameBuffer().showMessage(buf.str());
}
else
buf << "NTSC filtering not available";
buf << "TV filtering not available in software mode";
myOSystem->frameBuffer().showMessage(buf.str());
if(show) myOSystem->frameBuffer().showMessage(buf.str());
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Console::changeScanlines(int amount, bool show)
{
ostringstream buf;
if(myOSystem->frameBuffer().type() == kDoubleBuffer)
{
if(myOSystem->frameBuffer().ntscEnabled())
{
uInt32 intensity = myOSystem->frameBuffer().changeScanlines(amount);
buf << "Scanline intensity at " << intensity << "%";
myOSystem->settings().setInt("tv_scanlines", intensity);
}
else
buf << "Scanlines only available in TV filtering mode";
}
else
buf << "Scanlines not available in software mode";
if(show) myOSystem->frameBuffer().showMessage(buf.str());
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -488,7 +517,8 @@ FBInitStatus Console::initializeVideo(bool full)
bool enable = myProperties.get(Display_Phosphor) == "YES";
int blend = atoi(myProperties.get(Display_PPBlend).c_str());
myOSystem->frameBuffer().enablePhosphor(enable, blend);
myOSystem->frameBuffer().enableNTSC(myOSystem->settings().getBool("ntsc_filter"));
myOSystem->frameBuffer().changeScanlines(0, myOSystem->settings().getInt("tv_scanlines"));
toggleNTSC((NTSCFilter::Preset)myOSystem->settings().getInt("tv_filter"));
setPalette(myOSystem->settings().getString("palette"));
// Set the correct framerate based on the format of the ROM

View File

@ -35,6 +35,7 @@ class CompuMate;
#include "TIATables.hxx"
#include "FrameBuffer.hxx"
#include "Serializable.hxx"
#include "NTSCFilter.hxx"
/**
Contains detailed info about a console.
@ -202,9 +203,14 @@ class Console : public Serializable
void togglePhosphor();
/**
Toggles NTSC filtering effects.
Toggles NTSC filtering effects to use the given preset.
*/
void toggleNTSC();
void toggleNTSC(NTSCFilter::Preset preset, bool show = false);
/**
Increase/decrease current scanline intensity by given amount.
*/
void changeScanlines(int amount, bool show = false);
/**
Toggles the PAL color-loss effect.

View File

@ -378,8 +378,35 @@ void EventHandler::poll(uInt64 time)
myOSystem->console().changeYStart(-1);
break;
case KBDK_f: // Alt-f toggles NTSC filtering
myOSystem->console().toggleNTSC();
case KBDK_1: // Alt-1 turns off NTSC filtering
myOSystem->console().toggleNTSC(NTSCFilter::PRESET_OFF, true);
break;
case KBDK_2: // Alt-2 turns on 'composite' NTSC filtering
myOSystem->console().toggleNTSC(NTSCFilter::PRESET_COMPOSITE, true);
break;
case KBDK_3: // Alt-3 turns on 'svideo' NTSC filtering
myOSystem->console().toggleNTSC(NTSCFilter::PRESET_SVIDEO, true);
break;
case KBDK_4: // Alt-4 turns on 'rgb' NTSC filtering
myOSystem->console().toggleNTSC(NTSCFilter::PRESET_RGB, true);
break;
case KBDK_5: // Alt-5 turns on 'bad' NTSC filtering
myOSystem->console().toggleNTSC(NTSCFilter::PRESET_BAD, true);
break;
case KBDK_6: // Alt-6 turns on 'custom' NTSC filtering
myOSystem->console().toggleNTSC(NTSCFilter::PRESET_CUSTOM, true);
break;
case KBDK_7: // Alt-7 changes scanline intensity for NTSC filtering
if(mod & KMOD_SHIFT)
myOSystem->console().changeScanlines(-5, true);
else
myOSystem->console().changeScanlines(+5, true);
break;
case KBDK_z:
@ -440,7 +467,9 @@ void EventHandler::poll(uInt64 time)
break;
case KBDK_p: // Alt-p toggles phosphor effect
myOSystem->console().togglePhosphor();
// Currently, phosphor mode cannot be enabled with NTSC filtering
if(!myOSystem->frameBuffer().ntscEnabled())
myOSystem->console().togglePhosphor();
break;
case KBDK_l:

View File

@ -36,6 +36,7 @@ namespace GUI {
#include "EventHandler.hxx"
#include "Rect.hxx"
#include "StringList.hxx"
#include "NTSCFilter.hxx"
#include "bspf.hxx"
// Different types of framebuffer derived objects
@ -285,12 +286,37 @@ class FrameBuffer
void stateChanged(EventHandler::State state);
/**
Get the zoom level.
Get the NTSCFilter object associated with the framebuffer
*/
uInt32 getZoomLevel() { return myZoomLevel; }
NTSCFilter& ntsc() { return myNTSCFilter; }
//////////////////////////////////////////////////////////////////////
// The following methods are system-specific and must be implemented
// The following methods are system-specific and *may* be implemented
// in derived classes.
//////////////////////////////////////////////////////////////////////
public:
/**
Enable/disable NTSC filtering effects.
*/
virtual void enableNTSC(bool enable) { }
virtual bool ntscEnabled() const { return false; }
/**
Change scanline intensity.
relative = -1 means decrease current intensity by 'directin
direction = 0 means to reload the current video mode
direction = +1 means go to the next higher video mode
@param relative If non-zero, change current intensity by
'relative' amount, otherwise set to 'absolute'
@return New current intensity
*/
virtual uInt32 changeScanlines(int relative, int absolute = 50) { return absolute; }
//////////////////////////////////////////////////////////////////////
// The following methods are system-specific and *must* be implemented
// in derived classes.
//////////////////////////////////////////////////////////////////////
public:
@ -299,11 +325,6 @@ class FrameBuffer
*/
virtual void enablePhosphor(bool enable, int blend) = 0;
/**
Enable/disable NTSC filtering effects.
*/
virtual void enableNTSC(bool enable) = 0;
/**
This method is called to retrieve the R/G/B data from the given pixel.
@ -374,6 +395,14 @@ class FrameBuffer
uInt32 image_x, image_y, image_w, image_h;
uInt32 screen_w, screen_h;
GraphicsMode gfxmode;
friend ostream& operator<<(ostream& os, const VideoMode& vm)
{
os << "image_x=" << vm.image_x << " image_y=" << vm.image_y
<< " image_w=" << vm.image_w << " image_h=" << vm.image_h << endl
<< "screen_w=" << vm.screen_w << " screen_h=" << vm.screen_h;
return os;
}
};
/**
@ -451,6 +480,9 @@ class FrameBuffer
// Indicates if the entire frame need to redrawn
bool myRedrawEntireFrame;
// NTSC object to use in TIA rendering mode
NTSCFilter myNTSCFilter;
// Use phosphor effect (aka no flicker on 30Hz screens)
bool myUsePhosphor;
@ -470,9 +502,6 @@ class FrameBuffer
// Names of the TIA filters that can be used for this framebuffer
StringMap myTIAFilters;
// Holds the zoom level being used
uInt32 myZoomLevel;
private:
/**
Grabs or ungrabs the mouse based on the given boolean value.

View File

@ -43,7 +43,7 @@ Settings::Settings(OSystem* osystem)
setInternal("gl_filter", "nearest");
setInternal("gl_aspectn", "90");
setInternal("gl_aspectp", "100");
setInternal("gl_fsmax", "true");
setInternal("gl_fsscale", "false");
setInternal("gl_lib", "libGL.so");
setInternal("gl_vsync", "true");
setInternal("gl_vbo", "true");
@ -59,8 +59,9 @@ Settings::Settings(OSystem* osystem)
setInternal("timing", "sleep");
setInternal("uimessages", "true");
// NTSC filtering options
setInternal("ntsc_filter", "false");
// TV filtering options
setInternal("tv_filter", "0");
setInternal("tv_scanlines", "50");
// Sound options
setInternal("sound", "true");
@ -265,6 +266,10 @@ void Settings::validate()
if(i < 80 || i > 120) setInternal("gl_aspectn", "100");
i = getInt("gl_aspectp");
if(i < 80 || i > 120) setInternal("gl_aspectp", "100");
i = getInt("tv_filter");
if(i < 0 || i > 5) setInternal("tv_filter", "0");
#endif
#ifdef SOUND_SUPPORT
@ -335,11 +340,12 @@ void Settings::usage()
<< " linear Blurred scaling (GL_LINEAR)\n"
<< " -gl_aspectn <number> Scale the TIA width by the given percentage in NTSC mode\n"
<< " -gl_aspectp <number> Scale the TIA width by the given percentage in PAL mode\n"
<< " -gl_fsmax <1|0> Stretch GL image in fullscreen emulation mode\n"
<< " -gl_fsscale <1|0> Stretch GL image in fullscreen emulation mode to max/integer scale\n"
<< " -gl_vsync <1|0> Enable 'synchronize to vertical blank interrupt'\n"
<< " -gl_vbo <1|0> Enable 'vertex buffer objects'\n"
<< endl
<< " -ntsc_filter <1|0> Enable Blargg NTSC filtering effects\n"
<< " -tv_filter <0-5> Set TV effects off (0) or to specified mode (1-5)\n"
<< " -tv_scanlines <20-100> Set scanline intensity to percentage\n"
<< endl
#endif
<< " -tia_filter <filter> Use the specified filter in emulation mode\n"

View File

@ -346,7 +346,7 @@ void VideoDialog::loadConfig()
myColorLossCheckbox->setState(instance().settings().getBool("colorloss"));
// GL stretch setting (GL mode only)
myGLStretchCheckbox->setState(instance().settings().getBool("gl_fsmax"));
myGLStretchCheckbox->setState(instance().settings().getBool("gl_fsscale"));
myGLStretchCheckbox->setEnabled(gl);
// Use sync to vertical blank (GL mode only)
@ -409,7 +409,7 @@ void VideoDialog::saveConfig()
instance().console().toggleColorLoss(myColorLossCheckbox->getState());
// GL stretch setting
instance().settings().setBool("gl_fsmax", myGLStretchCheckbox->getState());
instance().settings().setBool("gl_fsscale", myGLStretchCheckbox->getState());
// Use sync to vertical blank (GL mode only)
instance().settings().setBool("gl_vsync", myUseVSyncCheckbox->getState());