mirror of https://github.com/stella-emu/stella.git
Get rid of smoothing in favor of scaling settings.
This commit is contained in:
parent
31408864ee
commit
c4d1473f81
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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%)
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in New Issue