From c4d1473f81c9773c51656a90d4a0de0c889eba58 Mon Sep 17 00:00:00 2001 From: Christian Speckner Date: Fri, 13 Dec 2019 22:46:08 +0100 Subject: [PATCH] Get rid of smoothing in favor of scaling settings. --- src/common/FBSurfaceSDL2.cxx | 26 ++++++++++++++-- src/common/FBSurfaceSDL2.hxx | 2 ++ src/common/FrameBufferSDL2.cxx | 10 ++++-- src/common/FrameBufferSDL2.hxx | 14 ++++++--- src/common/sdl_blitter/BilinearBlitter.cxx | 5 +-- src/common/sdl_blitter/BilinearBlitter.hxx | 3 +- src/common/sdl_blitter/BlitterFactory.cxx | 18 +++++++++-- src/common/sdl_blitter/BlitterFactory.hxx | 12 +++++++- src/common/sdl_blitter/HqBlitter.cxx | 2 +- src/emucore/FBSurface.cxx | 3 +- src/emucore/FBSurface.hxx | 1 - src/emucore/FrameBuffer.cxx | 4 +-- src/emucore/FrameBuffer.hxx | 36 ++++++++++++++++------ src/emucore/TIASurface.cxx | 19 +++++------- src/gui/RomInfoWidget.cxx | 1 - src/libretro/FrameBufferLIBRETRO.cxx | 2 +- src/libretro/FrameBufferLIBRETRO.hxx | 2 +- 17 files changed, 115 insertions(+), 45 deletions(-) diff --git a/src/common/FBSurfaceSDL2.cxx b/src/common/FBSurfaceSDL2.cxx index f5ee9ff68..552de3df5 100644 --- a/src/common/FBSurfaceSDL2.cxx +++ b/src/common/FBSurfaceSDL2.cxx @@ -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); } diff --git a/src/common/FBSurfaceSDL2.hxx b/src/common/FBSurfaceSDL2.hxx index 42da59616..523181a9d 100644 --- a/src/common/FBSurfaceSDL2.hxx +++ b/src/common/FBSurfaceSDL2.hxx @@ -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 myBlitter; + const FrameBuffer::ScalingInterpolation myInterpolationMode; SDL_Surface* mySurface; SDL_Rect mySrcR, myDstR; diff --git a/src/common/FrameBufferSDL2.cxx b/src/common/FrameBufferSDL2.cxx index 400ef30ce..1dd6d8efa 100644 --- a/src/common/FrameBufferSDL2.cxx +++ b/src/common/FrameBufferSDL2.cxx @@ -402,10 +402,14 @@ void FrameBufferSDL2::setWindowIcon() } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -unique_ptr - FrameBufferSDL2::createSurface(uInt32 w, uInt32 h, const uInt32* data) const +unique_ptr FrameBufferSDL2::createSurface( + uInt32 w, + uInt32 h, + FrameBuffer::ScalingInterpolation interpolation, + const uInt32* data +) const { - return make_unique(const_cast(*this), w, h, data); + return make_unique(const_cast(*this), w, h, interpolation, data); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/common/FrameBufferSDL2.hxx b/src/common/FrameBufferSDL2.hxx index a444e7e33..9959af90c 100644 --- a/src/common/FrameBufferSDL2.hxx +++ b/src/common/FrameBufferSDL2.hxx @@ -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 - 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. diff --git a/src/common/sdl_blitter/BilinearBlitter.cxx b/src/common/sdl_blitter/BilinearBlitter.cxx index b283a1ba3..6be1efbc7 100644 --- a/src/common/sdl_blitter/BilinearBlitter.cxx +++ b/src/common/sdl_blitter/BilinearBlitter.cxx @@ -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); diff --git a/src/common/sdl_blitter/BilinearBlitter.hxx b/src/common/sdl_blitter/BilinearBlitter.hxx index 1fd4151d7..08225fe94 100644 --- a/src/common/sdl_blitter/BilinearBlitter.hxx +++ b/src/common/sdl_blitter/BilinearBlitter.hxx @@ -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; diff --git a/src/common/sdl_blitter/BlitterFactory.cxx b/src/common/sdl_blitter/BlitterFactory.cxx index 0a35ba62f..636af8fb3 100644 --- a/src/common/sdl_blitter/BlitterFactory.cxx +++ b/src/common/sdl_blitter/BlitterFactory.cxx @@ -21,12 +21,24 @@ #include "BilinearBlitter.hxx" #include "HqBlitter.hxx" -unique_ptr BlitterFactory::createBlitter(FrameBufferSDL2& fb) +unique_ptr BlitterFactory::createBlitter(FrameBufferSDL2& fb, ScalingAlgorithm scaling) { if (!fb.isInitialized()) { throw runtime_error("BlitterFactory requires an initialized framebuffer!"); } - return HqBlitter::isSupported(fb) ? - unique_ptr(new HqBlitter(fb)) : unique_ptr(new BilinearBlitter(fb)); + switch (scaling) { + case ScalingAlgorithm::nearestNeighbour: + return unique_ptr(new BilinearBlitter(fb, false)); + + case ScalingAlgorithm::bilinear: + return unique_ptr(new BilinearBlitter(fb, true)); + + case ScalingAlgorithm::quasiInteger: + return HqBlitter::isSupported(fb) ? + unique_ptr(new HqBlitter(fb)) : unique_ptr(new BilinearBlitter(fb, true)); + + default: + throw runtime_error("unreachable"); + } } diff --git a/src/common/sdl_blitter/BlitterFactory.hxx b/src/common/sdl_blitter/BlitterFactory.hxx index c653292c7..6bd9e02ba 100644 --- a/src/common/sdl_blitter/BlitterFactory.hxx +++ b/src/common/sdl_blitter/BlitterFactory.hxx @@ -18,6 +18,8 @@ #ifndef BLITTER_FACTORY_HXX #define BLITTER_FACTORY_HXX +#include + #include "Blitter.hxx" #include "bspf.hxx" #include "FrameBufferSDL2.hxx" @@ -25,7 +27,15 @@ class BlitterFactory { public: - static unique_ptr createBlitter(FrameBufferSDL2& fb); + enum class ScalingAlgorithm { + nearestNeighbour, + bilinear, + quasiInteger + }; + + public: + + static unique_ptr createBlitter(FrameBufferSDL2& fb, ScalingAlgorithm scaling); }; #endif // BLITTER_FACTORY_HXX diff --git a/src/common/sdl_blitter/HqBlitter.cxx b/src/common/sdl_blitter/HqBlitter.cxx index 2ec1f16dc..16eb00551 100644 --- a/src/common/sdl_blitter/HqBlitter.cxx +++ b/src/common/sdl_blitter/HqBlitter.cxx @@ -170,7 +170,7 @@ void HqBlitter::recreateTexturesIfNecessary() myBlankBuffer = make_unique(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); diff --git a/src/emucore/FBSurface.cxx b/src/emucore/FBSurface.cxx index f38965344..7b1f18c95 100644 --- a/src/emucore/FBSurface.cxx +++ b/src/emucore/FBSurface.cxx @@ -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; } diff --git a/src/emucore/FBSurface.hxx b/src/emucore/FBSurface.hxx index f287e4b85..8b503f56d 100644 --- a/src/emucore/FBSurface.hxx +++ b/src/emucore/FBSurface.hxx @@ -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%) }; diff --git a/src/emucore/FrameBuffer.cxx b/src/emucore/FrameBuffer.cxx index 1fcabaff7..286cd2a6b 100644 --- a/src/emucore/FrameBuffer.cxx +++ b/src/emucore/FrameBuffer.cxx @@ -643,10 +643,10 @@ void FrameBuffer::setPauseDelay() } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -shared_ptr FrameBuffer::allocateSurface(int w, int h, const uInt32* data) +shared_ptr 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); diff --git a/src/emucore/FrameBuffer.hxx b/src/emucore/FrameBuffer.hxx index bcd485495..7e7885457 100644 --- a/src/emucore/FrameBuffer.hxx +++ b/src/emucore/FrameBuffer.hxx @@ -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 allocateSurface(int w, int h, const uInt32* data = nullptr); + shared_ptr 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 - 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. diff --git a/src/emucore/TIASurface.cxx b/src/emucore/TIASurface.cxx index c7bde5422..f6d8ddeed 100644 --- a/src/emucore/TIASurface.cxx +++ b/src/emucore/TIASurface.cxx @@ -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(); diff --git a/src/gui/RomInfoWidget.cxx b/src/gui/RomInfoWidget.cxx index 4028f8b0d..397c8ad35 100644 --- a/src/gui/RomInfoWidget.cxx +++ b/src/gui/RomInfoWidget.cxx @@ -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); diff --git a/src/libretro/FrameBufferLIBRETRO.cxx b/src/libretro/FrameBufferLIBRETRO.cxx index ed64a028b..85840b0a4 100644 --- a/src/libretro/FrameBufferLIBRETRO.cxx +++ b/src/libretro/FrameBufferLIBRETRO.cxx @@ -44,7 +44,7 @@ void FrameBufferLIBRETRO::queryHardware(vector& fullscreenRes, // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - unique_ptr - FrameBufferLIBRETRO::createSurface(uInt32 w, uInt32 h, const uInt32* data) const + FrameBufferLIBRETRO::createSurface(uInt32 w, uInt32 h, FrameBuffer::ScalingInterpolation, const uInt32* data) const { unique_ptr ptr = make_unique (const_cast(*this), w, h, data); diff --git a/src/libretro/FrameBufferLIBRETRO.hxx b/src/libretro/FrameBufferLIBRETRO.hxx index 359cda4c5..dc504bcd5 100644 --- a/src/libretro/FrameBufferLIBRETRO.hxx +++ b/src/libretro/FrameBufferLIBRETRO.hxx @@ -147,7 +147,7 @@ class FrameBufferLIBRETRO : public FrameBuffer @param data If non-null, use the given data values as a static surface */ unique_ptr - 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.