Get rid of smoothing in favor of scaling settings.

This commit is contained in:
Christian Speckner 2019-12-13 22:46:08 +01:00
parent 31408864ee
commit c4d1473f81
17 changed files with 115 additions and 45 deletions

View File

@ -20,10 +20,32 @@
#include "ThreadDebugging.hxx" #include "ThreadDebugging.hxx"
#include "sdl_blitter/BlitterFactory.hxx" #include "sdl_blitter/BlitterFactory.hxx"
namespace {
BlitterFactory::ScalingAlgorithm scalingAlgorithm(FrameBuffer::ScalingInterpolation interpolation)
{
switch (interpolation) {
case FrameBuffer::ScalingInterpolation::none:
return BlitterFactory::ScalingAlgorithm::nearestNeighbour;
case FrameBuffer::ScalingInterpolation::blur:
return BlitterFactory::ScalingAlgorithm::bilinear;
case FrameBuffer::ScalingInterpolation::sharp:
return BlitterFactory::ScalingAlgorithm::quasiInteger;
default:
throw runtime_error("unreachable");
}
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FBSurfaceSDL2::FBSurfaceSDL2(FrameBufferSDL2& buffer, FBSurfaceSDL2::FBSurfaceSDL2(FrameBufferSDL2& buffer,
uInt32 width, uInt32 height, const uInt32* data) uInt32 width, uInt32 height,
FrameBuffer::ScalingInterpolation interpolation,
const uInt32* data)
: myFB(buffer), : myFB(buffer),
myInterpolationMode(interpolation),
mySurface(nullptr), mySurface(nullptr),
myIsVisible(true), myIsVisible(true),
myIsStatic(false) myIsStatic(false)
@ -215,7 +237,7 @@ void FBSurfaceSDL2::createSurface(uInt32 width, uInt32 height,
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceSDL2::reinitializeBlitter() void FBSurfaceSDL2::reinitializeBlitter()
{ {
if (!myBlitter && myFB.isInitialized()) myBlitter = BlitterFactory::createBlitter(myFB); if (!myBlitter && myFB.isInitialized()) myBlitter = BlitterFactory::createBlitter(myFB, scalingAlgorithm(myInterpolationMode));
if (myBlitter) myBlitter->reinitialize(mySrcR, myDstR, myAttributes, myIsStatic ? mySurface : nullptr); if (myBlitter) myBlitter->reinitialize(mySrcR, myDstR, myAttributes, myIsStatic ? mySurface : nullptr);
} }

View File

@ -33,6 +33,7 @@ class FBSurfaceSDL2 : public FBSurface
{ {
public: public:
FBSurfaceSDL2(FrameBufferSDL2& buffer, uInt32 width, uInt32 height, FBSurfaceSDL2(FrameBufferSDL2& buffer, uInt32 width, uInt32 height,
FrameBuffer::ScalingInterpolation interpolation,
const uInt32* data); const uInt32* data);
virtual ~FBSurfaceSDL2(); virtual ~FBSurfaceSDL2();
@ -80,6 +81,7 @@ class FBSurfaceSDL2 : public FBSurface
FrameBufferSDL2& myFB; FrameBufferSDL2& myFB;
unique_ptr<Blitter> myBlitter; unique_ptr<Blitter> myBlitter;
const FrameBuffer::ScalingInterpolation myInterpolationMode;
SDL_Surface* mySurface; SDL_Surface* mySurface;
SDL_Rect mySrcR, myDstR; SDL_Rect mySrcR, myDstR;

View File

@ -402,10 +402,14 @@ void FrameBufferSDL2::setWindowIcon()
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
unique_ptr<FBSurface> unique_ptr<FBSurface> FrameBufferSDL2::createSurface(
FrameBufferSDL2::createSurface(uInt32 w, uInt32 h, const uInt32* data) const uInt32 w,
uInt32 h,
FrameBuffer::ScalingInterpolation interpolation,
const uInt32* data
) const
{ {
return make_unique<FBSurfaceSDL2>(const_cast<FrameBufferSDL2&>(*this), w, h, data); return make_unique<FBSurfaceSDL2>(const_cast<FrameBufferSDL2&>(*this), w, h, interpolation, data);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -161,10 +161,16 @@ class FrameBufferSDL2 : public FrameBuffer
@param w The requested width of the new surface. @param w The requested width of the new surface.
@param h The requested height of the new surface. @param h The requested height of the new surface.
@param interpolation Interpolation mode
@param data If non-null, use the given data values as a static surface @param data If non-null, use the given data values as a static surface
*/ */
unique_ptr<FBSurface> unique_ptr<FBSurface>
createSurface(uInt32 w, uInt32 h, const uInt32* data) const override; createSurface(
uInt32 w,
uInt32 h,
FrameBuffer::ScalingInterpolation interpolation,
const uInt32* data
) const override;
/** /**
Grabs or ungrabs the mouse based on the given boolean value. Grabs or ungrabs the mouse based on the given boolean value.

View File

@ -20,9 +20,10 @@
#include "ThreadDebugging.hxx" #include "ThreadDebugging.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
BilinearBlitter::BilinearBlitter(FrameBufferSDL2& fb) : BilinearBlitter::BilinearBlitter(FrameBufferSDL2& fb, bool interpolate) :
myTexture(nullptr), myTexture(nullptr),
mySecondaryTexture(nullptr), mySecondaryTexture(nullptr),
myInterpolate(interpolate),
myTexturesAreAllocated(false), myTexturesAreAllocated(false),
myRecreateTextures(false), myRecreateTextures(false),
myStaticData(nullptr), myStaticData(nullptr),
@ -111,7 +112,7 @@ void BilinearBlitter::recreateTexturesIfNecessary()
SDL_TextureAccess texAccess = myStaticData == nullptr ? SDL_TEXTUREACCESS_STREAMING : SDL_TEXTUREACCESS_STATIC; SDL_TextureAccess texAccess = myStaticData == nullptr ? SDL_TEXTUREACCESS_STREAMING : SDL_TEXTUREACCESS_STATIC;
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, myAttributes.smoothing ? "1" : "0"); SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, myInterpolate ? "1" : "0");
myTexture = SDL_CreateTexture(myFB.renderer(), myFB.pixelFormat().format, myTexture = SDL_CreateTexture(myFB.renderer(), myFB.pixelFormat().format,
texAccess, mySrcRect.w, mySrcRect.h); texAccess, mySrcRect.w, mySrcRect.h);

View File

@ -26,7 +26,7 @@ class BilinearBlitter : public Blitter {
public: public:
BilinearBlitter(FrameBufferSDL2& fb); BilinearBlitter(FrameBufferSDL2& fb, bool interpolate);
virtual ~BilinearBlitter(); virtual ~BilinearBlitter();
@ -48,6 +48,7 @@ class BilinearBlitter : public Blitter {
SDL_Rect mySrcRect, myDstRect; SDL_Rect mySrcRect, myDstRect;
FBSurface::Attributes myAttributes; FBSurface::Attributes myAttributes;
bool myInterpolate;
bool myTexturesAreAllocated; bool myTexturesAreAllocated;
bool myRecreateTextures; bool myRecreateTextures;

View File

@ -21,12 +21,24 @@
#include "BilinearBlitter.hxx" #include "BilinearBlitter.hxx"
#include "HqBlitter.hxx" #include "HqBlitter.hxx"
unique_ptr<Blitter> BlitterFactory::createBlitter(FrameBufferSDL2& fb) unique_ptr<Blitter> BlitterFactory::createBlitter(FrameBufferSDL2& fb, ScalingAlgorithm scaling)
{ {
if (!fb.isInitialized()) { if (!fb.isInitialized()) {
throw runtime_error("BlitterFactory requires an initialized framebuffer!"); throw runtime_error("BlitterFactory requires an initialized framebuffer!");
} }
switch (scaling) {
case ScalingAlgorithm::nearestNeighbour:
return unique_ptr<Blitter>(new BilinearBlitter(fb, false));
case ScalingAlgorithm::bilinear:
return unique_ptr<Blitter>(new BilinearBlitter(fb, true));
case ScalingAlgorithm::quasiInteger:
return HqBlitter::isSupported(fb) ? return HqBlitter::isSupported(fb) ?
unique_ptr<Blitter>(new HqBlitter(fb)) : unique_ptr<Blitter>(new BilinearBlitter(fb)); unique_ptr<Blitter>(new HqBlitter(fb)) : unique_ptr<Blitter>(new BilinearBlitter(fb, true));
default:
throw runtime_error("unreachable");
}
} }

View File

@ -18,6 +18,8 @@
#ifndef BLITTER_FACTORY_HXX #ifndef BLITTER_FACTORY_HXX
#define BLITTER_FACTORY_HXX #define BLITTER_FACTORY_HXX
#include <string>
#include "Blitter.hxx" #include "Blitter.hxx"
#include "bspf.hxx" #include "bspf.hxx"
#include "FrameBufferSDL2.hxx" #include "FrameBufferSDL2.hxx"
@ -25,7 +27,15 @@
class BlitterFactory { class BlitterFactory {
public: public:
static unique_ptr<Blitter> createBlitter(FrameBufferSDL2& fb); enum class ScalingAlgorithm {
nearestNeighbour,
bilinear,
quasiInteger
};
public:
static unique_ptr<Blitter> createBlitter(FrameBufferSDL2& fb, ScalingAlgorithm scaling);
}; };
#endif // BLITTER_FACTORY_HXX #endif // BLITTER_FACTORY_HXX

View File

@ -170,7 +170,7 @@ void HqBlitter::recreateTexturesIfNecessary()
myBlankBuffer = make_unique<uInt32[]>(4 * myIntermediateRect.w * myIntermediateRect.h); myBlankBuffer = make_unique<uInt32[]>(4 * myIntermediateRect.w * myIntermediateRect.h);
memset(myBlankBuffer.get(), 0, 4 * myIntermediateRect.w * myIntermediateRect.h); memset(myBlankBuffer.get(), 0, 4 * myIntermediateRect.w * myIntermediateRect.h);
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, myAttributes.smoothing ? "1" : "0"); SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1");
myIntermediateTexture = SDL_CreateTexture(myFB.renderer(), myFB.pixelFormat().format, myIntermediateTexture = SDL_CreateTexture(myFB.renderer(), myFB.pixelFormat().format,
SDL_TEXTUREACCESS_TARGET, myIntermediateRect.w, myIntermediateRect.h); SDL_TEXTUREACCESS_TARGET, myIntermediateRect.w, myIntermediateRect.h);

View File

@ -34,7 +34,6 @@ FBSurface::FBSurface()
// from this class // from this class
// Set default attributes // Set default attributes
myAttributes.smoothing = false;
myAttributes.blending = false; myAttributes.blending = false;
myAttributes.blendalpha = 100; myAttributes.blendalpha = 100;
} }
@ -455,5 +454,5 @@ const uInt32* FBSurface::myPalette = nullptr;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool operator==(const FBSurface::Attributes& a1, const FBSurface::Attributes& a2) bool operator==(const FBSurface::Attributes& a1, const FBSurface::Attributes& a2)
{ {
return a1.blendalpha == a2.blendalpha && a1.blending == a2.blending && a1.smoothing && a2.smoothing; return a1.blendalpha == a2.blendalpha && a1.blending == a2.blending;
} }

View File

@ -326,7 +326,6 @@ class FBSurface
the specific functionality actually exists. the specific functionality actually exists.
*/ */
struct Attributes { struct Attributes {
bool smoothing; // Scaling is smoothed or blocky
bool blending; // Blending is enabled bool blending; // Blending is enabled
uInt32 blendalpha; // Alpha to use in blending mode (0-100%) uInt32 blendalpha; // Alpha to use in blending mode (0-100%)
}; };

View File

@ -643,10 +643,10 @@ void FrameBuffer::setPauseDelay()
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
shared_ptr<FBSurface> FrameBuffer::allocateSurface(int w, int h, const uInt32* data) shared_ptr<FBSurface> FrameBuffer::allocateSurface(int w, int h, ScalingInterpolation interpolation, const uInt32* data)
{ {
// Add new surface to the list // Add new surface to the list
mySurfaceList.push_back(createSurface(w, h, data)); mySurfaceList.push_back(createSurface(w, h, interpolation, data));
// And return a pointer to it (pointer should be treated read-only) // And return a pointer to it (pointer should be treated read-only)
return mySurfaceList.at(mySurfaceList.size() - 1); return mySurfaceList.at(mySurfaceList.size() - 1);

View File

@ -81,6 +81,12 @@ class FrameBuffer
} }
}; };
enum class ScalingInterpolation {
none,
sharp,
blur
};
// Zoom level step interval // Zoom level step interval
static constexpr float ZOOM_STEPS = 0.25; static constexpr float ZOOM_STEPS = 0.25;
@ -166,11 +172,17 @@ class FrameBuffer
@param w The requested width of the new surface. @param w The requested width of the new surface.
@param h The requested height of the new surface. @param h The requested height of the new surface.
@param interpolation Interpolation mode
@param data If non-null, use the given data values as a static surface @param data If non-null, use the given data values as a static surface
@return A pointer to a valid surface object, or nullptr. @return A pointer to a valid surface object, or nullptr.
*/ */
shared_ptr<FBSurface> allocateSurface(int w, int h, const uInt32* data = nullptr); shared_ptr<FBSurface> allocateSurface(
int w,
int h,
ScalingInterpolation interpolation = ScalingInterpolation::none,
const uInt32* data = nullptr
);
/** /**
Returns the current dimensions of the framebuffer image. Returns the current dimensions of the framebuffer image.
@ -395,13 +407,19 @@ class FrameBuffer
/** /**
This method is called to create a surface with the given attributes. This method is called to create a surface with the given attributes.
z
@param w The requested width of the new surface. @param w The requested width of the new surface.
@param h The requested height of the new surface. @param h The requested height of the new surface.
@param interpolation Interpolation mode
@param data If non-null, use the given data values as a static surface @param data If non-null, use the given data values as a static surface
*/ */
virtual unique_ptr<FBSurface> virtual unique_ptr<FBSurface>
createSurface(uInt32 w, uInt32 h, const uInt32* data) const = 0; createSurface(
uInt32 w,
uInt32 h,
ScalingInterpolation interpolation = ScalingInterpolation::none,
const uInt32* data = nullptr
) const = 0;
/** /**
Calls 'free()' on all surfaces that the framebuffer knows about. Calls 'free()' on all surfaces that the framebuffer knows about.

View File

@ -41,8 +41,11 @@ TIASurface::TIASurface(OSystem& system)
myNTSCFilter.loadConfig(myOSystem.settings()); myNTSCFilter.loadConfig(myOSystem.settings());
// Create a surface for the TIA image and scanlines; we'll need them eventually // Create a surface for the TIA image and scanlines; we'll need them eventually
myTiaSurface = myFB.allocateSurface(AtariNTSC::outWidth(TIAConstants::frameBufferWidth), myTiaSurface = myFB.allocateSurface(
TIAConstants::frameBufferHeight); AtariNTSC::outWidth(TIAConstants::frameBufferWidth),
TIAConstants::frameBufferHeight,
FrameBuffer::ScalingInterpolation::sharp
);
// Generate scanline data, and a pre-defined scanline surface // Generate scanline data, and a pre-defined scanline surface
constexpr uInt32 scanHeight = TIAConstants::frameBufferHeight * 2; constexpr uInt32 scanHeight = TIAConstants::frameBufferHeight * 2;
@ -52,7 +55,7 @@ TIASurface::TIASurface(OSystem& system)
scanData[i] = 0x00000000; scanData[i] = 0x00000000;
scanData[i+1] = 0xff000000; scanData[i+1] = 0xff000000;
} }
mySLineSurface = myFB.allocateSurface(1, scanHeight, scanData); mySLineSurface = myFB.allocateSurface(1, scanHeight, FrameBuffer::ScalingInterpolation::sharp, scanData);
// Base TIA surface for use in taking snapshots in 1x mode // Base TIA surface for use in taking snapshots in 1x mode
myBaseTiaSurface = myFB.allocateSurface(TIAConstants::frameBufferWidth*2, myBaseTiaSurface = myFB.allocateSurface(TIAConstants::frameBufferWidth*2,
@ -255,13 +258,8 @@ void TIASurface::enableNTSC(bool enable)
myTiaSurface->setSrcSize(enable ? AtariNTSC::outWidth(TIAConstants::frameBufferWidth) myTiaSurface->setSrcSize(enable ? AtariNTSC::outWidth(TIAConstants::frameBufferWidth)
: TIAConstants::frameBufferWidth, myTIA->height()); : TIAConstants::frameBufferWidth, myTIA->height());
FBSurface::Attributes& tia_attr = myTiaSurface->attributes();
tia_attr.smoothing = myOSystem.settings().getBool("tia.inter");
myTiaSurface->applyAttributes();
myScanlinesEnabled = myOSystem.settings().getInt("tv.scanlines") > 0; myScanlinesEnabled = myOSystem.settings().getInt("tv.scanlines") > 0;
FBSurface::Attributes& sl_attr = mySLineSurface->attributes(); FBSurface::Attributes& sl_attr = mySLineSurface->attributes();
sl_attr.smoothing = true;
sl_attr.blending = myScanlinesEnabled; sl_attr.blending = myScanlinesEnabled;
sl_attr.blendalpha = myOSystem.settings().getInt("tv.scanlines"); sl_attr.blendalpha = myOSystem.settings().getInt("tv.scanlines");
mySLineSurface->applyAttributes(); mySLineSurface->applyAttributes();
@ -284,12 +282,11 @@ string TIASurface::effectsInfo() const
buf << "Disabled, phosphor mode"; buf << "Disabled, phosphor mode";
break; break;
case Filter::BlarggNormal: case Filter::BlarggNormal:
buf << myNTSCFilter.getPreset() << ", scanlines=" << attr.blendalpha << "/" buf << myNTSCFilter.getPreset() << ", scanlines=" << attr.blendalpha;
<< (attr.smoothing ? "inter" : "nointer");
break; break;
case Filter::BlarggPhosphor: case Filter::BlarggPhosphor:
buf << myNTSCFilter.getPreset() << ", phosphor, scanlines=" buf << myNTSCFilter.getPreset() << ", phosphor, scanlines="
<< attr.blendalpha << "/" << (attr.smoothing ? "inter" : "nointer"); << attr.blendalpha;
break; break;
} }
return buf.str(); return buf.str();

View File

@ -87,7 +87,6 @@ void RomInfoWidget::parseProperties(const FilesystemNode& node)
{ {
mySurface = instance().frameBuffer().allocateSurface( mySurface = instance().frameBuffer().allocateSurface(
TIAConstants::viewableWidth*2, TIAConstants::viewableHeight*2); TIAConstants::viewableWidth*2, TIAConstants::viewableHeight*2);
mySurface->attributes().smoothing = true;
mySurface->applyAttributes(); mySurface->applyAttributes();
dialog().addSurface(mySurface); dialog().addSurface(mySurface);

View File

@ -44,7 +44,7 @@ void FrameBufferLIBRETRO::queryHardware(vector<Common::Size>& fullscreenRes,
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
unique_ptr<FBSurface> unique_ptr<FBSurface>
FrameBufferLIBRETRO::createSurface(uInt32 w, uInt32 h, const uInt32* data) const FrameBufferLIBRETRO::createSurface(uInt32 w, uInt32 h, FrameBuffer::ScalingInterpolation, const uInt32* data) const
{ {
unique_ptr<FBSurface> ptr = make_unique<FBSurfaceLIBRETRO> unique_ptr<FBSurface> ptr = make_unique<FBSurfaceLIBRETRO>
(const_cast<FrameBufferLIBRETRO&>(*this), w, h, data); (const_cast<FrameBufferLIBRETRO&>(*this), w, h, data);

View File

@ -147,7 +147,7 @@ class FrameBufferLIBRETRO : public FrameBuffer
@param data If non-null, use the given data values as a static surface @param data If non-null, use the given data values as a static surface
*/ */
unique_ptr<FBSurface> unique_ptr<FBSurface>
createSurface(uInt32 w, uInt32 h, const uInt32* data) const override; createSurface(uInt32 w, uInt32 h, FrameBuffer::ScalingInterpolation, const uInt32* data) const override;
/** /**
Grabs or ungrabs the mouse based on the given boolean value. Grabs or ungrabs the mouse based on the given boolean value.