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 "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,
uInt32 width, uInt32 height, const uInt32* data)
uInt32 width, uInt32 height,
FrameBuffer::ScalingInterpolation interpolation,
const uInt32* data)
: myFB(buffer),
myInterpolationMode(interpolation),
mySurface(nullptr),
myIsVisible(true),
myIsStatic(false)
@ -215,7 +237,7 @@ void FBSurfaceSDL2::createSurface(uInt32 width, uInt32 height,
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
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);
}

View File

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

View File

@ -402,10 +402,14 @@ void FrameBufferSDL2::setWindowIcon()
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
unique_ptr<FBSurface>
FrameBufferSDL2::createSurface(uInt32 w, uInt32 h, const uInt32* data) const
unique_ptr<FBSurface> FrameBufferSDL2::createSurface(
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

@ -159,12 +159,18 @@ class FrameBufferSDL2 : public FrameBuffer
/**
This method is called to create a surface with the given attributes.
@param w The requested width of the new surface.
@param h The requested height of the new surface.
@param data If non-null, use the given data values as a static surface
@param w The requested width 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
*/
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.

View File

@ -20,9 +20,10 @@
#include "ThreadDebugging.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
BilinearBlitter::BilinearBlitter(FrameBufferSDL2& fb) :
BilinearBlitter::BilinearBlitter(FrameBufferSDL2& fb, bool interpolate) :
myTexture(nullptr),
mySecondaryTexture(nullptr),
myInterpolate(interpolate),
myTexturesAreAllocated(false),
myRecreateTextures(false),
myStaticData(nullptr),
@ -111,7 +112,7 @@ void BilinearBlitter::recreateTexturesIfNecessary()
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,
texAccess, mySrcRect.w, mySrcRect.h);

View File

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

View File

@ -21,12 +21,24 @@
#include "BilinearBlitter.hxx"
#include "HqBlitter.hxx"
unique_ptr<Blitter> BlitterFactory::createBlitter(FrameBufferSDL2& fb)
unique_ptr<Blitter> BlitterFactory::createBlitter(FrameBufferSDL2& fb, ScalingAlgorithm scaling)
{
if (!fb.isInitialized()) {
throw runtime_error("BlitterFactory requires an initialized framebuffer!");
}
return HqBlitter::isSupported(fb) ?
unique_ptr<Blitter>(new HqBlitter(fb)) : unique_ptr<Blitter>(new BilinearBlitter(fb));
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) ?
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
#define BLITTER_FACTORY_HXX
#include <string>
#include "Blitter.hxx"
#include "bspf.hxx"
#include "FrameBufferSDL2.hxx"
@ -25,7 +27,15 @@
class BlitterFactory {
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

View File

@ -170,7 +170,7 @@ void HqBlitter::recreateTexturesIfNecessary()
myBlankBuffer = make_unique<uInt32[]>(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,
SDL_TEXTUREACCESS_TARGET, myIntermediateRect.w, myIntermediateRect.h);

View File

@ -34,7 +34,6 @@ FBSurface::FBSurface()
// from this class
// Set default attributes
myAttributes.smoothing = false;
myAttributes.blending = false;
myAttributes.blendalpha = 100;
}
@ -455,5 +454,5 @@ const uInt32* FBSurface::myPalette = nullptr;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
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.
*/
struct Attributes {
bool smoothing; // Scaling is smoothed or blocky
bool blending; // Blending is enabled
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
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)
return mySurfaceList.at(mySurfaceList.size() - 1);

View File

@ -81,6 +81,12 @@ class FrameBuffer
}
};
enum class ScalingInterpolation {
none,
sharp,
blur
};
// Zoom level step interval
static constexpr float ZOOM_STEPS = 0.25;
@ -164,13 +170,19 @@ class FrameBuffer
Allocate a new surface. The FrameBuffer class takes all responsibility
for freeing this surface (ie, other classes must not delete it directly).
@param w The requested width of the new surface.
@param h The requested height of the new surface.
@param data If non-null, use the given data values as a static surface
@param w The requested width 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
@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.
@ -395,13 +407,19 @@ class FrameBuffer
/**
This method is called to create a surface with the given attributes.
@param w The requested width of the new surface.
@param h The requested height of the new surface.
@param data If non-null, use the given data values as a static surface
z
@param w The requested width 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
*/
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.

View File

@ -41,8 +41,11 @@ 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(AtariNTSC::outWidth(TIAConstants::frameBufferWidth),
TIAConstants::frameBufferHeight);
myTiaSurface = myFB.allocateSurface(
AtariNTSC::outWidth(TIAConstants::frameBufferWidth),
TIAConstants::frameBufferHeight,
FrameBuffer::ScalingInterpolation::sharp
);
// Generate scanline data, and a pre-defined scanline surface
constexpr uInt32 scanHeight = TIAConstants::frameBufferHeight * 2;
@ -52,7 +55,7 @@ TIASurface::TIASurface(OSystem& system)
scanData[i] = 0x00000000;
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
myBaseTiaSurface = myFB.allocateSurface(TIAConstants::frameBufferWidth*2,
@ -255,13 +258,8 @@ void TIASurface::enableNTSC(bool enable)
myTiaSurface->setSrcSize(enable ? AtariNTSC::outWidth(TIAConstants::frameBufferWidth)
: 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;
FBSurface::Attributes& sl_attr = mySLineSurface->attributes();
sl_attr.smoothing = true;
sl_attr.blending = myScanlinesEnabled;
sl_attr.blendalpha = myOSystem.settings().getInt("tv.scanlines");
mySLineSurface->applyAttributes();
@ -284,12 +282,11 @@ string TIASurface::effectsInfo() const
buf << "Disabled, phosphor mode";
break;
case Filter::BlarggNormal:
buf << myNTSCFilter.getPreset() << ", scanlines=" << attr.blendalpha << "/"
<< (attr.smoothing ? "inter" : "nointer");
buf << myNTSCFilter.getPreset() << ", scanlines=" << attr.blendalpha;
break;
case Filter::BlarggPhosphor:
buf << myNTSCFilter.getPreset() << ", phosphor, scanlines="
<< attr.blendalpha << "/" << (attr.smoothing ? "inter" : "nointer");
<< attr.blendalpha;
break;
}
return buf.str();

View File

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

View File

@ -44,7 +44,7 @@ void FrameBufferLIBRETRO::queryHardware(vector<Common::Size>& fullscreenRes,
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
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>
(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
*/
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.