[Scummvm-git-logs] scummvm branch-2-5 -> 216bcc3ae3cf50efb6e0577f1d7e96913eacf391

aquadran noreply at scummvm.org
Thu Dec 9 21:00:39 UTC 2021


This automated email contains information about 9 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
f3453a6d12 GRAPHICS: Fix invalid memory write in the DotMatrix scaler
bf54f65931 GRAPHICS: Add generic functions for converting palettes
01b2b381c8 OPENGL: Combine TextureCLUT8 and FakeTexture
73aab0accf OPENGL: Implement scaler support
b94f24aa67 ANDROID: Enable scalers by default
b8f2c0b990 GRAPHICS: Split ScalerPluginObject into two classes
00203d7ef9 OPENGL: Restore cursor scaling
95bc1c46ed OPENGL: Fix crash when scaling small areas
216bcc3ae3 NEWS: Mention scaler support in OpenGL mode


Commit: f3453a6d12021c6f0e5a2e05168a55115b4d9065
    https://github.com/scummvm/scummvm/commit/f3453a6d12021c6f0e5a2e05168a55115b4d9065
Author: Cameron Cawley (ccawley2011 at gmail.com)
Date: 2021-12-09T22:00:32+01:00

Commit Message:
GRAPHICS: Fix invalid memory write in the DotMatrix scaler

Changed paths:
    graphics/scaler/dotmatrix.h


diff --git a/graphics/scaler/dotmatrix.h b/graphics/scaler/dotmatrix.h
index 27dbc1267d..dbdf62bfe5 100644
--- a/graphics/scaler/dotmatrix.h
+++ b/graphics/scaler/dotmatrix.h
@@ -39,7 +39,7 @@ protected:
 							uint8 *dstPtr, uint32 dstPitch, int width, int height, int x, int y) override;
 private:
 	// Allocate enough for 32bpp formats
-	uint32 lookup[16];
+	uint32 lookup[17];
 	template<typename Pixel>
 	void scaleIntern(const uint8 *srcPtr, uint32 srcPitch, uint8 *dstPtr,
 			uint32 dstPitch, int width, int height, int x, int y);


Commit: bf54f659317845c335b82326e2978d713f24e95f
    https://github.com/scummvm/scummvm/commit/bf54f659317845c335b82326e2978d713f24e95f
Author: Cameron Cawley (ccawley2011 at gmail.com)
Date: 2021-12-09T22:00:32+01:00

Commit Message:
GRAPHICS: Add generic functions for converting palettes

Changed paths:
    graphics/conversion.cpp
    graphics/conversion.h


diff --git a/graphics/conversion.cpp b/graphics/conversion.cpp
index f12db3dd67..cfef1a8454 100644
--- a/graphics/conversion.cpp
+++ b/graphics/conversion.cpp
@@ -135,6 +135,32 @@ inline void crossBlitLogic(byte *dst, const byte *src, const uint w, const uint
 	}
 }
 
+template<typename DstColor, bool backward>
+inline void crossBlitLogic1BppSource(byte *dst, const byte *src, const uint w, const uint h,
+									 const uint srcDelta, const uint dstDelta, const uint32 *map) {
+	for (uint y = 0; y < h; ++y) {
+		for (uint x = 0; x < w; ++x) {
+			*(DstColor *)dst = map[*src];
+
+			if (backward) {
+				src -= 1;
+				dst -= sizeof(DstColor);
+			} else {
+				src += 1;
+				dst += sizeof(DstColor);
+			}
+		}
+
+		if (backward) {
+			src -= srcDelta;
+			dst -= dstDelta;
+		} else {
+			src += srcDelta;
+			dst += dstDelta;
+		}
+	}
+}
+
 template<typename DstColor, bool backward>
 inline void crossBlitLogic3BppSource(byte *dst, const byte *src, const uint w, const uint h,
 									 const PixelFormat &srcFmt, const PixelFormat &dstFmt,
@@ -230,6 +256,45 @@ bool crossBlit(byte *dst, const byte *src,
 	return true;
 }
 
+// Function to blit a rect from one color format to another using a map
+bool crossBlitMap(byte *dst, const byte *src,
+			   const uint dstPitch, const uint srcPitch,
+			   const uint w, const uint h,
+			   const uint bytesPerPixel, const uint32 *map) {
+	// Error out if conversion is impossible
+	if ((bytesPerPixel == 3) || (!bytesPerPixel))
+		return false;
+
+	// Faster, but larger, to provide optimized handling for each case.
+	const uint srcDelta = (srcPitch - w);
+	const uint dstDelta = (dstPitch - w * bytesPerPixel);
+
+	if (bytesPerPixel == 1) {
+		crossBlitLogic1BppSource<uint8, false>(dst, src, w, h, srcDelta, dstDelta, map);
+	} else if (bytesPerPixel == 2) {
+		// We need to blit the surface from bottom right to top left here.
+		// This is neeeded, because when we convert to the same memory
+		// buffer copying the surface from top left to bottom right would
+		// overwrite the source, since we have more bits per destination
+		// color than per source color.
+		dst += h * dstPitch - dstDelta - bytesPerPixel;
+		src += h * srcPitch - srcDelta - 1;
+		crossBlitLogic1BppSource<uint16, true>(dst, src, w, h, srcDelta, dstDelta, map);
+	} else if (bytesPerPixel == 4) {
+		// We need to blit the surface from bottom right to top left here.
+		// This is neeeded, because when we convert to the same memory
+		// buffer copying the surface from top left to bottom right would
+		// overwrite the source, since we have more bits per destination
+		// color than per source color.
+		dst += h * dstPitch - dstDelta - bytesPerPixel;
+		src += h * srcPitch - srcDelta - 1;
+		crossBlitLogic1BppSource<uint32, true>(dst, src, w, h, srcDelta, dstDelta, map);
+	} else {
+		return false;
+	}
+	return true;
+}
+
 namespace {
 
 template <typename Size>
diff --git a/graphics/conversion.h b/graphics/conversion.h
index 75bc9aceb8..8d3a1312e2 100644
--- a/graphics/conversion.h
+++ b/graphics/conversion.h
@@ -25,6 +25,8 @@
 
 #include "common/util.h"
 
+#include "graphics/pixelformat.h"
+
 namespace Common {
 struct Point;
 }
@@ -40,7 +42,6 @@ namespace Graphics {
  * @{
  */
 
-struct PixelFormat;
 struct TransformStruct;
 
 /** Converting a color from YUV to RGB colorspace. */
@@ -57,6 +58,14 @@ inline static void RGB2YUV(byte r, byte g, byte b, byte &y, byte &u, byte &v) {
 	v = CLIP<int>( ((r * 512) >> 10) - ((g * 429) >> 10) - ((b *  83) >> 10) + 128, 0, 255);
 }
 
+/** Converting a palette for use with crossBlitMap(). */
+inline static void convertPaletteToMap(uint32 *dst, const byte *src, uint colors, const Graphics::PixelFormat &format) {
+	while (colors-- > 0) {
+		*dst++ = format.RGBToColor(src[0], src[1], src[2]);
+		src += 3;
+	}
+}
+
 // TODO: generic YUV to RGB blit
 
 /**
@@ -118,6 +127,11 @@ bool crossBlit(byte *dst, const byte *src,
 			   const uint w, const uint h,
 			   const Graphics::PixelFormat &dstFmt, const Graphics::PixelFormat &srcFmt);
 
+bool crossBlitMap(byte *dst, const byte *src,
+			   const uint dstPitch, const uint srcPitch,
+			   const uint w, const uint h,
+			   const uint bytesPerPixel, const uint32 *map);
+
 bool scaleBlit(byte *dst, const byte *src,
 			   const uint dstPitch, const uint srcPitch,
 			   const uint dstW, const uint dstH,


Commit: 01b2b381c8a45713556341467e8ae91c38bec9b1
    https://github.com/scummvm/scummvm/commit/01b2b381c8a45713556341467e8ae91c38bec9b1
Author: Cameron Cawley (ccawley2011 at gmail.com)
Date: 2021-12-09T22:00:32+01:00

Commit Message:
OPENGL: Combine TextureCLUT8 and FakeTexture

Changed paths:
    backends/graphics/opengl/opengl-graphics.cpp
    backends/graphics/opengl/texture.cpp
    backends/graphics/opengl/texture.h


diff --git a/backends/graphics/opengl/opengl-graphics.cpp b/backends/graphics/opengl/opengl-graphics.cpp
index ca1618e1b8..1b72066b8d 100644
--- a/backends/graphics/opengl/opengl-graphics.cpp
+++ b/backends/graphics/opengl/opengl-graphics.cpp
@@ -1105,7 +1105,7 @@ Surface *OpenGLGraphicsManager::createSurface(const Graphics::PixelFormat &forma
 		if (!supported) {
 			return nullptr;
 		} else {
-			return new TextureCLUT8(glIntFormat, glFormat, glType, virtFormat);
+			return new FakeTexture(glIntFormat, glFormat, glType, virtFormat, format);
 		}
 	} else if (getGLPixelFormat(format, glIntFormat, glFormat, glType)) {
 		return new Texture(glIntFormat, glFormat, glType, format);
diff --git a/backends/graphics/opengl/texture.cpp b/backends/graphics/opengl/texture.cpp
index 2cdaf92f37..58b3b2baa6 100644
--- a/backends/graphics/opengl/texture.cpp
+++ b/backends/graphics/opengl/texture.cpp
@@ -314,143 +314,60 @@ void Texture::updateGLTexture() {
 	clearDirty();
 }
 
-TextureCLUT8::TextureCLUT8(GLenum glIntFormat, GLenum glFormat, GLenum glType, const Graphics::PixelFormat &format)
-	: Texture(glIntFormat, glFormat, glType, format), _clut8Data(), _palette(new byte[256 * format.bytesPerPixel]) {
-	memset(_palette, 0, sizeof(byte) * format.bytesPerPixel);
+FakeTexture::FakeTexture(GLenum glIntFormat, GLenum glFormat, GLenum glType, const Graphics::PixelFormat &format, const Graphics::PixelFormat &fakeFormat)
+	: Texture(glIntFormat, glFormat, glType, format),
+	  _fakeFormat(fakeFormat),
+	  _rgbData(),
+	  _palette(nullptr) {
+	if (_fakeFormat == Graphics::PixelFormat::createFormatCLUT8()) {
+		_palette = new uint32[256];
+		memset(_palette, 0, sizeof(uint32));
+	}
 }
 
-TextureCLUT8::~TextureCLUT8() {
+FakeTexture::~FakeTexture() {
 	delete[] _palette;
 	_palette = nullptr;
-	_clut8Data.free();
+	_rgbData.free();
 }
 
-void TextureCLUT8::allocate(uint width, uint height) {
+void FakeTexture::allocate(uint width, uint height) {
 	Texture::allocate(width, height);
 
-	// We only need to reinitialize our CLUT8 surface when the output size
+	// We only need to reinitialize our surface when the output size
 	// changed.
-	if (width == (uint)_clut8Data.w && height == (uint)_clut8Data.h) {
+	if (width == (uint)_rgbData.w && height == (uint)_rgbData.h) {
 		return;
 	}
 
-	_clut8Data.create(width, height, Graphics::PixelFormat::createFormatCLUT8());
+	_rgbData.create(width, height, getFormat());
 }
 
-Graphics::PixelFormat TextureCLUT8::getFormat() const {
-	return Graphics::PixelFormat::createFormatCLUT8();
-}
+void FakeTexture::setColorKey(uint colorKey) {
+	if (!_palette)
+		return;
 
-void TextureCLUT8::setColorKey(uint colorKey) {
 	// The key color is set to black so the color value is pre-multiplied with the alpha value
 	// to avoid color fringes due to filtering.
 	// Erasing the color data is not a problem as the palette is always fully re-initialized
 	// before setting the key color.
-	if (_format.bytesPerPixel == 2) {
-		uint16 *palette = (uint16 *)_palette + colorKey;
-		*palette = 0;
-	} else if (_format.bytesPerPixel == 4) {
-		uint32 *palette = (uint32 *)_palette + colorKey;
-		*palette = 0;
-	} else {
-		warning("TextureCLUT8::setColorKey: Unsupported pixel depth %d", _format.bytesPerPixel);
-	}
+	uint32 *palette = _palette + colorKey;
+	*palette = 0;
 
 	// A palette changes means we need to refresh the whole surface.
 	flagDirty();
 }
 
-namespace {
-template<typename ColorType>
-inline void convertPalette(ColorType *dst, const byte *src, uint colors, const Graphics::PixelFormat &format) {
-	while (colors-- > 0) {
-		*dst++ = format.RGBToColor(src[0], src[1], src[2]);
-		src += 3;
-	}
-}
-} // End of anonymous namespace
+void FakeTexture::setPalette(uint start, uint colors, const byte *palData) {
+	if (!_palette)
+		return;
 
-void TextureCLUT8::setPalette(uint start, uint colors, const byte *palData) {
-	if (_format.bytesPerPixel == 2) {
-		convertPalette<uint16>((uint16 *)_palette + start, palData, colors, _format);
-	} else if (_format.bytesPerPixel == 4) {
-		convertPalette<uint32>((uint32 *)_palette + start, palData, colors, _format);
-	} else {
-		warning("TextureCLUT8::setPalette: Unsupported pixel depth: %d", _format.bytesPerPixel);
-	}
+	Graphics::convertPaletteToMap(_palette + start, palData, colors, _format);
 
 	// A palette changes means we need to refresh the whole surface.
 	flagDirty();
 }
 
-namespace {
-template<typename PixelType>
-inline void doPaletteLookUp(PixelType *dst, const byte *src, uint width, uint height, uint dstPitch, uint srcPitch, const PixelType *palette) {
-	uint srcAdd = srcPitch - width;
-	uint dstAdd = dstPitch - width * sizeof(PixelType);
-
-	while (height-- > 0) {
-		for (uint x = width; x > 0; --x) {
-			*dst++ = palette[*src++];
-		}
-
-		dst = (PixelType *)((byte *)dst + dstAdd);
-		src += srcAdd;
-	}
-}
-} // End of anonymous namespace
-
-void TextureCLUT8::updateGLTexture() {
-	if (!isDirty()) {
-		return;
-	}
-
-	// Do the palette look up
-	Graphics::Surface *outSurf = Texture::getSurface();
-
-	Common::Rect dirtyArea = getDirtyArea();
-
-	if (outSurf->format.bytesPerPixel == 2) {
-		doPaletteLookUp<uint16>((uint16 *)outSurf->getBasePtr(dirtyArea.left, dirtyArea.top),
-		                        (const byte *)_clut8Data.getBasePtr(dirtyArea.left, dirtyArea.top),
-		                        dirtyArea.width(), dirtyArea.height(),
-		                        outSurf->pitch, _clut8Data.pitch, (const uint16 *)_palette);
-	} else if (outSurf->format.bytesPerPixel == 4) {
-		doPaletteLookUp<uint32>((uint32 *)outSurf->getBasePtr(dirtyArea.left, dirtyArea.top),
-		                        (const byte *)_clut8Data.getBasePtr(dirtyArea.left, dirtyArea.top),
-		                        dirtyArea.width(), dirtyArea.height(),
-		                        outSurf->pitch, _clut8Data.pitch, (const uint32 *)_palette);
-	} else {
-		warning("TextureCLUT8::updateGLTexture: Unsupported pixel depth: %d", outSurf->format.bytesPerPixel);
-	}
-
-	// Do generic handling of updating the texture.
-	Texture::updateGLTexture();
-}
-
-FakeTexture::FakeTexture(GLenum glIntFormat, GLenum glFormat, GLenum glType, const Graphics::PixelFormat &format, const Graphics::PixelFormat &fakeFormat)
-	: Texture(glIntFormat, glFormat, glType, format),
-	  _fakeFormat(fakeFormat),
-	  _rgbData() {
-}
-
-FakeTexture::~FakeTexture() {
-	_rgbData.free();
-}
-
-void FakeTexture::allocate(uint width, uint height) {
-	Texture::allocate(width, height);
-
-	// We only need to reinitialize our surface when the output size
-	// changed.
-	if (width == (uint)_rgbData.w && height == (uint)_rgbData.h) {
-		return;
-	}
-
-	warning("%s pixel format not supported by OpenGL ES, using %s instead", getFormat().toString().c_str(), _format.toString().c_str());
-	_rgbData.create(width, height, getFormat());
-}
-
 void FakeTexture::updateGLTexture() {
 	if (!isDirty()) {
 		return;
@@ -463,7 +380,12 @@ void FakeTexture::updateGLTexture() {
 
 	byte *dst = (byte *)outSurf->getBasePtr(dirtyArea.left, dirtyArea.top);
 	const byte *src = (const byte *)_rgbData.getBasePtr(dirtyArea.left, dirtyArea.top);
-	Graphics::crossBlit(dst, src, outSurf->pitch, _rgbData.pitch, dirtyArea.width(), dirtyArea.height(), outSurf->format, _rgbData.format);
+
+	if (_palette) {
+		Graphics::crossBlitMap(dst, src, outSurf->pitch, _rgbData.pitch, dirtyArea.width(), dirtyArea.height(), outSurf->format.bytesPerPixel, _palette);
+	} else {
+		Graphics::crossBlit(dst, src, outSurf->pitch, _rgbData.pitch, dirtyArea.width(), dirtyArea.height(), outSurf->format, _rgbData.format);
+	}
 
 	// Do generic handling of updating the texture.
 	Texture::updateGLTexture();
diff --git a/backends/graphics/opengl/texture.h b/backends/graphics/opengl/texture.h
index aed42405f4..70c9feeb78 100644
--- a/backends/graphics/opengl/texture.h
+++ b/backends/graphics/opengl/texture.h
@@ -295,29 +295,6 @@ private:
 	Graphics::Surface _userPixelData;
 };
 
-class TextureCLUT8 : public Texture {
-public:
-	TextureCLUT8(GLenum glIntFormat, GLenum glFormat, GLenum glType, const Graphics::PixelFormat &format);
-	virtual ~TextureCLUT8();
-
-	virtual void allocate(uint width, uint height);
-
-	virtual Graphics::PixelFormat getFormat() const;
-
-	virtual bool hasPalette() const { return true; }
-
-	virtual void setColorKey(uint colorKey);
-	virtual void setPalette(uint start, uint colors, const byte *palData);
-
-	virtual Graphics::Surface *getSurface() { return &_clut8Data; }
-	virtual const Graphics::Surface *getSurface() const { return &_clut8Data; }
-
-	virtual void updateGLTexture();
-private:
-	Graphics::Surface _clut8Data;
-	byte *_palette;
-};
-
 class FakeTexture : public Texture {
 public:
 	FakeTexture(GLenum glIntFormat, GLenum glFormat, GLenum glType, const Graphics::PixelFormat &format, const Graphics::PixelFormat &fakeFormat);
@@ -327,6 +304,11 @@ public:
 
 	virtual Graphics::PixelFormat getFormat() const { return _fakeFormat; }
 
+	virtual bool hasPalette() const { return (_palette != nullptr); }
+
+	virtual void setColorKey(uint colorKey);
+	virtual void setPalette(uint start, uint colors, const byte *palData);
+
 	virtual Graphics::Surface *getSurface() { return &_rgbData; }
 	virtual const Graphics::Surface *getSurface() const { return &_rgbData; }
 
@@ -334,6 +316,7 @@ public:
 protected:
 	Graphics::Surface _rgbData;
 	Graphics::PixelFormat _fakeFormat;
+	uint32 *_palette;
 };
 
 class TextureRGB555 : public FakeTexture {


Commit: 73aab0accf286631c552fb25c588df08a687cd8b
    https://github.com/scummvm/scummvm/commit/73aab0accf286631c552fb25c588df08a687cd8b
Author: Cameron Cawley (ccawley2011 at gmail.com)
Date: 2021-12-09T22:00:32+01:00

Commit Message:
OPENGL: Implement scaler support

Changed paths:
    backends/graphics/opengl/opengl-graphics.cpp
    backends/graphics/opengl/opengl-graphics.h
    backends/graphics/opengl/texture.cpp
    backends/graphics/opengl/texture.h


diff --git a/backends/graphics/opengl/opengl-graphics.cpp b/backends/graphics/opengl/opengl-graphics.cpp
index 1b72066b8d..475c1a327c 100644
--- a/backends/graphics/opengl/opengl-graphics.cpp
+++ b/backends/graphics/opengl/opengl-graphics.cpp
@@ -45,6 +45,9 @@
 #include "graphics/fontman.h"
 #include "graphics/font.h"
 #endif
+#ifdef USE_SCALERS
+#include "graphics/scalerplugin.h"
+#endif
 
 #ifdef USE_PNG
 #include "image/png.h"
@@ -68,6 +71,9 @@ OpenGLGraphicsManager::OpenGLGraphicsManager()
 #ifdef USE_OSD
 	  , _osdMessageChangeRequest(false), _osdMessageAlpha(0), _osdMessageFadeStartTime(0), _osdMessageSurface(nullptr),
 	  _osdIconSurface(nullptr)
+#endif
+#ifdef USE_SCALERS
+	  , _scalerPlugins(ScalerMan.getPlugins())
 #endif
 	{
 	memset(_gamePalette, 0, sizeof(_gamePalette));
@@ -93,6 +99,9 @@ bool OpenGLGraphicsManager::hasFeature(OSystem::Feature f) const {
 	case OSystem::kFeatureCursorPalette:
 	case OSystem::kFeatureFilteringMode:
 	case OSystem::kFeatureStretchMode:
+#ifdef USE_SCALERS
+	case OSystem::kFeatureScalers:
+#endif
 		return true;
 
 	case OSystem::kFeatureOverlaySupportsAlpha:
@@ -287,6 +296,39 @@ int OpenGLGraphicsManager::getStretchMode() const {
 	return _stretchMode;
 }
 
+#ifdef USE_SCALERS
+uint OpenGLGraphicsManager::getDefaultScaler() const {
+	return ScalerMan.findScalerPluginIndex("normal");
+}
+
+uint OpenGLGraphicsManager::getDefaultScaleFactor() const {
+	return 1;
+}
+
+bool OpenGLGraphicsManager::setScaler(uint mode, int factor) {
+	assert(_transactionMode != kTransactionNone);
+
+	int newFactor;
+	if (factor == -1)
+		newFactor = getDefaultScaleFactor();
+	else if (_scalerPlugins[mode]->get<ScalerPluginObject>().hasFactor(factor))
+		newFactor = factor;
+	else if (_scalerPlugins[mode]->get<ScalerPluginObject>().hasFactor(_oldState.scaleFactor))
+		newFactor = _oldState.scaleFactor;
+	else
+		newFactor = _scalerPlugins[mode]->get<ScalerPluginObject>().getFactor();
+
+	_currentState.scalerIndex = mode;
+	_currentState.scaleFactor = newFactor;
+
+	return true;
+}
+
+uint OpenGLGraphicsManager::getScaler() const {
+	return _currentState.scalerIndex;
+}
+#endif
+
 void OpenGLGraphicsManager::beginGFXTransaction() {
 	assert(_transactionMode == kTransactionNone);
 
@@ -320,6 +362,13 @@ OSystem::TransactionError OpenGLGraphicsManager::endGFXTransaction() {
 	}
 #endif
 
+#ifdef USE_SCALERS
+	if (_oldState.scaleFactor != _currentState.scaleFactor ||
+	    _oldState.scalerIndex != _currentState.scalerIndex) {
+		setupNewGameScreen = true;
+	}
+#endif
+
 	do {
 		const uint desiredAspect = getDesiredGameAspectRatio();
 		const uint requestedWidth  = _currentState.gameWidth;
@@ -365,6 +414,11 @@ OSystem::TransactionError OpenGLGraphicsManager::endGFXTransaction() {
 					if (_oldState.filtering != _currentState.filtering) {
 						transactionError |= OSystem::kTransactionFilteringFailed;
 					}
+#ifdef USE_SCALERS
+					if (_oldState.scalerIndex != _currentState.scalerIndex) {
+						transactionError |= OSystem::kTransactionModeSwitchFailed;
+					}
+#endif
 
 					// Roll back to the old state.
 					_currentState = _oldState;
@@ -388,19 +442,29 @@ OSystem::TransactionError OpenGLGraphicsManager::endGFXTransaction() {
 	} while (_transactionMode == kTransactionRollback);
 
 	if (setupNewGameScreen) {
+		if (_gameScreen)
+			_gameScreen->unloadScaler();
 		delete _gameScreen;
 		_gameScreen = nullptr;
 
+		bool wantScaler = _currentState.scaleFactor > 1;
+
 #ifdef USE_RGB_COLOR
-		_gameScreen = createSurface(_currentState.gameFormat);
+		_gameScreen = createSurface(_currentState.gameFormat, false, wantScaler);
 #else
-		_gameScreen = createSurface(Graphics::PixelFormat::createFormatCLUT8());
+		_gameScreen = createSurface(Graphics::PixelFormat::createFormatCLUT8(), false, wantScaler);
 #endif
 		assert(_gameScreen);
 		if (_gameScreen->hasPalette()) {
 			_gameScreen->setPalette(0, 256, _gamePalette);
 		}
 
+#ifdef USE_SCALERS
+		if (wantScaler) {
+			_gameScreen->setScaler(_currentState.scalerIndex, _currentState.scaleFactor);
+		}
+#endif
+
 		_gameScreen->allocate(_currentState.gameWidth, _currentState.gameHeight);
 		_gameScreen->enableLinearFiltering(_currentState.filtering);
 		// We fill the screen to all black or index 0 for CLUT8.
@@ -727,6 +791,7 @@ void OpenGLGraphicsManager::setMouseCursor(const void *buf, uint w, uint h, int
 		} else {
 			textureFormat = _defaultFormatAlpha;
 		}
+		// TODO: Enable SW scaling for cursors
 		_cursor = createSurface(textureFormat, true);
 		assert(_cursor);
 		_cursor->enableLinearFiltering(_currentState.filtering);
@@ -1091,8 +1156,24 @@ void OpenGLGraphicsManager::notifyContextDestroy() {
 	g_context.reset();
 }
 
-Surface *OpenGLGraphicsManager::createSurface(const Graphics::PixelFormat &format, bool wantAlpha) {
+Surface *OpenGLGraphicsManager::createSurface(const Graphics::PixelFormat &format, bool wantAlpha, bool wantScaler) {
 	GLenum glIntFormat, glFormat, glType;
+
+#ifdef USE_SCALERS
+	if (wantScaler) {
+		// TODO: Ensure that the requested pixel format is supported by the scaler
+		if (getGLPixelFormat(format, glIntFormat, glFormat, glType)) {
+			return new ScaledTexture(glIntFormat, glFormat, glType, format, format);
+		} else {
+#ifdef SCUMM_LITTLE_ENDIAN
+			return new ScaledTexture(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24), format);
+#else
+			return new ScaledTexture(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0), format);
+#endif
+		}
+	}
+#endif
+
 	if (format.bytesPerPixel == 1) {
 #if !USE_FORCED_GLES
 		if (TextureCLUT8GPU::isSupportedByContext()) {
@@ -1231,6 +1312,10 @@ bool OpenGLGraphicsManager::gameNeedsAspectRatioCorrection() const {
 	return false;
 }
 
+int OpenGLGraphicsManager::getGameRenderScale() const {
+	return _currentState.scaleFactor;
+}
+
 void OpenGLGraphicsManager::recalculateDisplayAreas() {
 	if (!_gameScreen) {
 		return;
diff --git a/backends/graphics/opengl/opengl-graphics.h b/backends/graphics/opengl/opengl-graphics.h
index fc724c9334..9253412944 100644
--- a/backends/graphics/opengl/opengl-graphics.h
+++ b/backends/graphics/opengl/opengl-graphics.h
@@ -79,6 +79,13 @@ public:
 	virtual bool setStretchMode(int mode) override;
 	virtual int getStretchMode() const override;
 
+#ifdef USE_SCALERS
+	virtual uint getDefaultScaler() const override;
+	virtual uint getDefaultScaleFactor() const override;
+	virtual bool setScaler(uint mode, int factor) override;
+	virtual uint getScaler() const override;
+#endif
+
 	virtual void beginGFXTransaction() override;
 	virtual OSystem::TransactionError endGFXTransaction() override;
 
@@ -157,13 +164,14 @@ protected:
 	/**
 	 * Create a surface with the specified pixel format.
 	 *
-	 * @param format    The pixel format the Surface object should accept as
-	 *                  input.
-	 * @param wantAlpha For CLUT8 surfaces this marks whether an alpha
-	 *                  channel should be used.
+	 * @param format     The pixel format the Surface object should accept as
+	 *                   input.
+	 * @param wantAlpha  For CLUT8 surfaces this marks whether an alpha
+	 *                   channel should be used.
+	 * @param wantScaler Whether or not a software scaler should be used.
 	 * @return A pointer to the surface or nullptr on failure.
 	 */
-	Surface *createSurface(const Graphics::PixelFormat &format, bool wantAlpha = false);
+	Surface *createSurface(const Graphics::PixelFormat &format, bool wantAlpha = false, bool wantScaler = false);
 
 	//
 	// Transaction support
@@ -173,7 +181,8 @@ protected:
 #ifdef USE_RGB_COLOR
 		    gameFormat(),
 #endif
-		    aspectRatioCorrection(false), graphicsMode(GFX_OPENGL), filtering(true) {
+		    aspectRatioCorrection(false), graphicsMode(GFX_OPENGL), filtering(true),
+		    scalerIndex(0), scaleFactor(1) {
 		}
 
 		bool valid;
@@ -186,6 +195,9 @@ protected:
 		int graphicsMode;
 		bool filtering;
 
+		uint scalerIndex;
+		int scaleFactor;
+
 		bool operator==(const VideoState &right) {
 			return gameWidth == right.gameWidth && gameHeight == right.gameHeight
 #ifdef USE_RGB_COLOR
@@ -310,6 +322,7 @@ protected:
 	bool getGLPixelFormat(const Graphics::PixelFormat &pixelFormat, GLenum &glIntFormat, GLenum &glFormat, GLenum &glType) const;
 
 	virtual bool gameNeedsAspectRatioCorrection() const override;
+	virtual int getGameRenderScale() const override;
 	virtual void recalculateDisplayAreas() override;
 	virtual void handleResizeImpl(const int width, const int height) override;
 
@@ -414,6 +427,13 @@ protected:
 	 */
 	byte _cursorPalette[3 * 256];
 
+#ifdef USE_SCALERS
+	/**
+	 * The list of scaler plugins
+	 */
+	const PluginList &_scalerPlugins;
+#endif
+
 #ifdef USE_OSD
 	//
 	// OSD
diff --git a/backends/graphics/opengl/texture.cpp b/backends/graphics/opengl/texture.cpp
index 58b3b2baa6..a8d88c6afc 100644
--- a/backends/graphics/opengl/texture.cpp
+++ b/backends/graphics/opengl/texture.cpp
@@ -33,6 +33,10 @@
 
 #include "graphics/conversion.h"
 
+#ifdef USE_SCALERS
+#include "graphics/scalerplugin.h"
+#endif
+
 namespace OpenGL {
 
 GLTexture::GLTexture(GLenum glIntFormat, GLenum glFormat, GLenum glType)
@@ -279,6 +283,10 @@ void Texture::updateGLTexture() {
 
 	Common::Rect dirtyArea = getDirtyArea();
 
+	updateGLTexture(dirtyArea);
+}
+
+void Texture::updateGLTexture(Common::Rect &dirtyArea) {
 	// In case we use linear filtering we might need to duplicate the last
 	// pixel row/column to avoid glitches with filtering.
 	if (_glTexture.isLinearFilteringEnabled()) {
@@ -468,6 +476,109 @@ void TextureRGBA8888Swap::updateGLTexture() {
 	Texture::updateGLTexture();
 }
 
+#ifdef USE_SCALERS
+
+ScaledTexture::ScaledTexture(GLenum glIntFormat, GLenum glFormat, GLenum glType, const Graphics::PixelFormat &format, const Graphics::PixelFormat &fakeFormat)
+	: FakeTexture(glIntFormat, glFormat, glType, format, fakeFormat), _convData(nullptr), _scalerPlugin(nullptr), _scaleFactor(1), _extraPixels(0) {
+}
+
+ScaledTexture::~ScaledTexture() {
+	if (_convData) {
+		_convData->free();
+		delete _convData;
+	}
+}
+
+void ScaledTexture::allocate(uint width, uint height) {
+	Texture::allocate(width * _scaleFactor, height * _scaleFactor);
+
+	// We only need to reinitialize our surface when the output size
+	// changed.
+	if (width != (uint)_rgbData.w || height != (uint)_rgbData.h) {
+		_rgbData.create(width, height, _fakeFormat);
+	}
+
+	if (_format != _fakeFormat || _extraPixels != 0) {
+		if (!_convData)
+			_convData = new Graphics::Surface();
+
+		_convData->create(width + (_extraPixels * 2), height + (_extraPixels * 2), _format);
+	} else if (_convData) {
+		_convData->free();
+		delete _convData;
+		_convData = nullptr;
+	}
+}
+
+void ScaledTexture::updateGLTexture() {
+	if (!isDirty()) {
+		return;
+	}
+
+	// Convert color space.
+	Graphics::Surface *outSurf = Texture::getSurface();
+
+	Common::Rect dirtyArea = getDirtyArea();
+
+	const byte *src = (const byte *)_rgbData.getBasePtr(dirtyArea.left, dirtyArea.top);
+	uint srcPitch = _rgbData.pitch;
+	byte *dst;
+	uint dstPitch;
+
+	if (_convData) {
+		dst = (byte *)_convData->getBasePtr(dirtyArea.left + _extraPixels, dirtyArea.top + _extraPixels);
+		dstPitch = _convData->pitch;
+
+		if (_palette) {
+			Graphics::crossBlitMap(dst, src, dstPitch, srcPitch, dirtyArea.width(), dirtyArea.height(), _convData->format.bytesPerPixel, _palette);
+		} else {
+			Graphics::crossBlit(dst, src, dstPitch, srcPitch, dirtyArea.width(), dirtyArea.height(), _convData->format, _rgbData.format);
+		}
+
+		src = dst;
+		srcPitch = dstPitch;
+	}
+
+	dst = (byte *)outSurf->getBasePtr(dirtyArea.left * _scaleFactor, dirtyArea.top * _scaleFactor);
+	dstPitch = outSurf->pitch;
+
+	assert(_scalerPlugin);
+	_scalerPlugin->scale(src, srcPitch, dst, dstPitch, dirtyArea.width(), dirtyArea.height(), dirtyArea.left, dirtyArea.top);
+
+	dirtyArea.left   *= _scaleFactor;
+	dirtyArea.right  *= _scaleFactor;
+	dirtyArea.top    *= _scaleFactor;
+	dirtyArea.bottom *= _scaleFactor;
+
+	// Do generic handling of updating the texture.
+	Texture::updateGLTexture(dirtyArea);
+}
+
+void ScaledTexture::setScaler(uint scalerIndex, int scaleFactor) {
+	const PluginList &scalerPlugins = ScalerMan.getPlugins();
+
+	// If the scalerIndex has changed, change scaler plugins
+	if (&scalerPlugins[scalerIndex]->get<ScalerPluginObject>() != _scalerPlugin) {
+		if (_scalerPlugin)
+			_scalerPlugin->deinitialize();
+
+		_scalerPlugin = &scalerPlugins[scalerIndex]->get<ScalerPluginObject>();
+		_scalerPlugin->initialize(_format);
+	}
+	_scalerPlugin->setFactor(scaleFactor);
+
+	_scaleFactor = _scalerPlugin->getFactor();
+	_extraPixels = _scalerPlugin->extraPixels();
+}
+
+void ScaledTexture::unloadScaler() {
+	if (_scalerPlugin) {
+		_scalerPlugin->deinitialize();
+		_scalerPlugin = nullptr;
+	}
+}
+#endif
+
 #if !USE_FORCED_GLES
 
 // _clut8Texture needs 8 bits internal precision, otherwise graphics glitches
diff --git a/backends/graphics/opengl/texture.h b/backends/graphics/opengl/texture.h
index 70c9feeb78..aaaa968adb 100644
--- a/backends/graphics/opengl/texture.h
+++ b/backends/graphics/opengl/texture.h
@@ -30,6 +30,8 @@
 
 #include "common/rect.h"
 
+class ScalerPluginObject;
+
 namespace OpenGL {
 
 class Shader;
@@ -229,6 +231,9 @@ public:
 	virtual void setColorKey(uint colorKey) {}
 	virtual void setPalette(uint start, uint colors, const byte *palData) {}
 
+	virtual void setScaler(uint scalerIndex, int scaleFactor) {}
+	virtual void unloadScaler() {}
+
 	/**
 	 * Update underlying OpenGL texture to reflect current state.
 	 */
@@ -288,6 +293,8 @@ public:
 protected:
 	const Graphics::PixelFormat _format;
 
+	void updateGLTexture(Common::Rect &dirtyArea);
+
 private:
 	GLTexture _glTexture;
 
@@ -335,6 +342,35 @@ public:
 	virtual void updateGLTexture();
 };
 
+#ifdef USE_SCALERS
+class ScaledTexture : public FakeTexture {
+public:
+	ScaledTexture(GLenum glIntFormat, GLenum glFormat, GLenum glType, const Graphics::PixelFormat &format, const Graphics::PixelFormat &fakeFormat);
+	virtual ~ScaledTexture();
+
+	virtual void allocate(uint width, uint height);
+
+	virtual uint getWidth() const { return _rgbData.w; }
+	virtual uint getHeight() const { return _rgbData.h; }
+	virtual Graphics::PixelFormat getFormat() const { return _fakeFormat; }
+
+	virtual bool hasPalette() const { return (_palette != nullptr); }
+
+	virtual Graphics::Surface *getSurface() { return &_rgbData; }
+	virtual const Graphics::Surface *getSurface() const { return &_rgbData; }
+
+	virtual void updateGLTexture();
+
+	virtual void setScaler(uint scalerIndex, int scaleFactor);
+	virtual void unloadScaler();
+protected:
+	Graphics::Surface *_convData;
+	ScalerPluginObject *_scalerPlugin;
+	uint _extraPixels;
+	uint _scaleFactor;
+};
+#endif
+
 #if !USE_FORCED_GLES
 class TextureTarget;
 class CLUT8LookUpPipeline;


Commit: b94f24aa67742946bc6efb46129dab96b25cd3b4
    https://github.com/scummvm/scummvm/commit/b94f24aa67742946bc6efb46129dab96b25cd3b4
Author: Cameron Cawley (ccawley2011 at gmail.com)
Date: 2021-12-09T22:00:32+01:00

Commit Message:
ANDROID: Enable scalers by default

Changed paths:
    configure


diff --git a/configure b/configure
index 6fc50ec87d..65e4731474 100755
--- a/configure
+++ b/configure
@@ -3186,7 +3186,6 @@ if test -n "$_host"; then
 				_backend="android"
 			fi
 			_port_mk="backends/platform/$_backend/android.mk"
-			_build_scalers=no
 			_build_aspect=no
 			_seq_midi=no
 			_timidity=no


Commit: b8f2c0b99068680b90b23c6a786be3299cf3288c
    https://github.com/scummvm/scummvm/commit/b8f2c0b99068680b90b23c6a786be3299cf3288c
Author: Cameron Cawley (ccawley2011 at gmail.com)
Date: 2021-12-09T22:00:32+01:00

Commit Message:
GRAPHICS: Split ScalerPluginObject into two classes

Changed paths:
    backends/graphics/graphics.h
    backends/graphics/opengl/opengl-graphics.cpp
    backends/graphics/opengl/opengl-graphics.h
    backends/graphics/opengl/texture.cpp
    backends/graphics/opengl/texture.h
    backends/graphics/surfacesdl/surfacesdl-graphics.cpp
    backends/graphics/surfacesdl/surfacesdl-graphics.h
    backends/modular-backend.cpp
    backends/modular-backend.h
    common/system.h
    graphics/scaler/dotmatrix.cpp
    graphics/scaler/dotmatrix.h
    graphics/scaler/edge.cpp
    graphics/scaler/edge.h
    graphics/scaler/hq.cpp
    graphics/scaler/hq.h
    graphics/scaler/normal.cpp
    graphics/scaler/normal.h
    graphics/scaler/pm.cpp
    graphics/scaler/pm.h
    graphics/scaler/sai.cpp
    graphics/scaler/sai.h
    graphics/scaler/scalebit.cpp
    graphics/scaler/scalebit.h
    graphics/scaler/tv.cpp
    graphics/scaler/tv.h
    graphics/scalerplugin.cpp
    graphics/scalerplugin.h
    gui/options.cpp


diff --git a/backends/graphics/graphics.h b/backends/graphics/graphics.h
index 3533a8bafe..9316aa8f67 100644
--- a/backends/graphics/graphics.h
+++ b/backends/graphics/graphics.h
@@ -67,6 +67,7 @@ public:
 	virtual uint getDefaultScaleFactor() const { return 1; }
 	virtual bool setScaler(uint mode, int factor) { return false; }
 	virtual uint getScaler() const { return 0; }
+	virtual uint getScaleFactor() const { return 1; }
 
 #ifdef USE_RGB_COLOR
 	virtual Graphics::PixelFormat getScreenFormat() const = 0;
diff --git a/backends/graphics/opengl/opengl-graphics.cpp b/backends/graphics/opengl/opengl-graphics.cpp
index 475c1a327c..9c691358f2 100644
--- a/backends/graphics/opengl/opengl-graphics.cpp
+++ b/backends/graphics/opengl/opengl-graphics.cpp
@@ -316,7 +316,7 @@ bool OpenGLGraphicsManager::setScaler(uint mode, int factor) {
 	else if (_scalerPlugins[mode]->get<ScalerPluginObject>().hasFactor(_oldState.scaleFactor))
 		newFactor = _oldState.scaleFactor;
 	else
-		newFactor = _scalerPlugins[mode]->get<ScalerPluginObject>().getFactor();
+		newFactor = _scalerPlugins[mode]->get<ScalerPluginObject>().getDefaultFactor();
 
 	_currentState.scalerIndex = mode;
 	_currentState.scaleFactor = newFactor;
@@ -327,6 +327,10 @@ bool OpenGLGraphicsManager::setScaler(uint mode, int factor) {
 uint OpenGLGraphicsManager::getScaler() const {
 	return _currentState.scalerIndex;
 }
+
+uint OpenGLGraphicsManager::getScaleFactor() const {
+	return _currentState.scaleFactor;
+}
 #endif
 
 void OpenGLGraphicsManager::beginGFXTransaction() {
@@ -442,8 +446,6 @@ OSystem::TransactionError OpenGLGraphicsManager::endGFXTransaction() {
 	} while (_transactionMode == kTransactionRollback);
 
 	if (setupNewGameScreen) {
-		if (_gameScreen)
-			_gameScreen->unloadScaler();
 		delete _gameScreen;
 		_gameScreen = nullptr;
 
diff --git a/backends/graphics/opengl/opengl-graphics.h b/backends/graphics/opengl/opengl-graphics.h
index 9253412944..ac314b5f70 100644
--- a/backends/graphics/opengl/opengl-graphics.h
+++ b/backends/graphics/opengl/opengl-graphics.h
@@ -84,6 +84,7 @@ public:
 	virtual uint getDefaultScaleFactor() const override;
 	virtual bool setScaler(uint mode, int factor) override;
 	virtual uint getScaler() const override;
+	virtual uint getScaleFactor() const override;
 #endif
 
 	virtual void beginGFXTransaction() override;
diff --git a/backends/graphics/opengl/texture.cpp b/backends/graphics/opengl/texture.cpp
index a8d88c6afc..f63068deef 100644
--- a/backends/graphics/opengl/texture.cpp
+++ b/backends/graphics/opengl/texture.cpp
@@ -479,10 +479,12 @@ void TextureRGBA8888Swap::updateGLTexture() {
 #ifdef USE_SCALERS
 
 ScaledTexture::ScaledTexture(GLenum glIntFormat, GLenum glFormat, GLenum glType, const Graphics::PixelFormat &format, const Graphics::PixelFormat &fakeFormat)
-	: FakeTexture(glIntFormat, glFormat, glType, format, fakeFormat), _convData(nullptr), _scalerPlugin(nullptr), _scaleFactor(1), _extraPixels(0) {
+	: FakeTexture(glIntFormat, glFormat, glType, format, fakeFormat), _convData(nullptr), _scaler(nullptr), _scalerIndex(0), _scaleFactor(1), _extraPixels(0) {
 }
 
 ScaledTexture::~ScaledTexture() {
+	delete _scaler;
+
 	if (_convData) {
 		_convData->free();
 		delete _convData;
@@ -542,8 +544,8 @@ void ScaledTexture::updateGLTexture() {
 	dst = (byte *)outSurf->getBasePtr(dirtyArea.left * _scaleFactor, dirtyArea.top * _scaleFactor);
 	dstPitch = outSurf->pitch;
 
-	assert(_scalerPlugin);
-	_scalerPlugin->scale(src, srcPitch, dst, dstPitch, dirtyArea.width(), dirtyArea.height(), dirtyArea.left, dirtyArea.top);
+	assert(_scaler);
+	_scaler->scale(src, srcPitch, dst, dstPitch, dirtyArea.width(), dirtyArea.height(), dirtyArea.left, dirtyArea.top);
 
 	dirtyArea.left   *= _scaleFactor;
 	dirtyArea.right  *= _scaleFactor;
@@ -556,26 +558,22 @@ void ScaledTexture::updateGLTexture() {
 
 void ScaledTexture::setScaler(uint scalerIndex, int scaleFactor) {
 	const PluginList &scalerPlugins = ScalerMan.getPlugins();
+	const ScalerPluginObject &scalerPlugin = scalerPlugins[scalerIndex]->get<ScalerPluginObject>();
 
 	// If the scalerIndex has changed, change scaler plugins
-	if (&scalerPlugins[scalerIndex]->get<ScalerPluginObject>() != _scalerPlugin) {
-		if (_scalerPlugin)
-			_scalerPlugin->deinitialize();
-
-		_scalerPlugin = &scalerPlugins[scalerIndex]->get<ScalerPluginObject>();
-		_scalerPlugin->initialize(_format);
+	if (_scaler && scalerIndex != _scalerIndex) {
+		delete _scaler;
+		_scaler = nullptr;
 	}
-	_scalerPlugin->setFactor(scaleFactor);
 
-	_scaleFactor = _scalerPlugin->getFactor();
-	_extraPixels = _scalerPlugin->extraPixels();
-}
-
-void ScaledTexture::unloadScaler() {
-	if (_scalerPlugin) {
-		_scalerPlugin->deinitialize();
-		_scalerPlugin = nullptr;
+	if (!_scaler) {
+		_scaler = scalerPlugin.createInstance(_format);
 	}
+	_scaler->setFactor(scaleFactor);
+
+	_scalerIndex = scalerIndex;
+	_scaleFactor = _scaler->getFactor();
+	_extraPixels = scalerPlugin.extraPixels();
 }
 #endif
 
diff --git a/backends/graphics/opengl/texture.h b/backends/graphics/opengl/texture.h
index aaaa968adb..b45e4b12b2 100644
--- a/backends/graphics/opengl/texture.h
+++ b/backends/graphics/opengl/texture.h
@@ -30,7 +30,7 @@
 
 #include "common/rect.h"
 
-class ScalerPluginObject;
+class Scaler;
 
 namespace OpenGL {
 
@@ -232,7 +232,6 @@ public:
 	virtual void setPalette(uint start, uint colors, const byte *palData) {}
 
 	virtual void setScaler(uint scalerIndex, int scaleFactor) {}
-	virtual void unloadScaler() {}
 
 	/**
 	 * Update underlying OpenGL texture to reflect current state.
@@ -362,10 +361,10 @@ public:
 	virtual void updateGLTexture();
 
 	virtual void setScaler(uint scalerIndex, int scaleFactor);
-	virtual void unloadScaler();
 protected:
 	Graphics::Surface *_convData;
-	ScalerPluginObject *_scalerPlugin;
+	Scaler *_scaler;
+	uint _scalerIndex;
 	uint _extraPixels;
 	uint _scaleFactor;
 };
diff --git a/backends/graphics/surfacesdl/surfacesdl-graphics.cpp b/backends/graphics/surfacesdl/surfacesdl-graphics.cpp
index df5750ec3c..2e54076a93 100644
--- a/backends/graphics/surfacesdl/surfacesdl-graphics.cpp
+++ b/backends/graphics/surfacesdl/surfacesdl-graphics.cpp
@@ -130,7 +130,7 @@ SurfaceSdlGraphicsManager::SurfaceSdlGraphicsManager(SdlEventSource *sdlEventSou
 	_enableFocusRectDebugCode(false), _enableFocusRect(false), _focusRect(),
 #endif
 	_transactionMode(kTransactionNone),
-	_scalerPlugins(ScalerMan.getPlugins()),
+	_scalerPlugins(ScalerMan.getPlugins()), _scalerPlugin(nullptr), _scaler(nullptr),
 	_needRestoreAfterOverlay(false) {
 
 	// allocate palette storage
@@ -151,7 +151,7 @@ SurfaceSdlGraphicsManager::SurfaceSdlGraphicsManager(SdlEventSource *sdlEventSou
 	_videoMode.aspectRatioCorrection = false;
 #endif
 
-	_scalerPlugin = NULL;
+	_scaler = NULL;
 	_maxExtraPixels = ScalerMan.getMaxExtraPixels();
 
 	_videoMode.fullscreen = ConfMan.getBool("fullscreen");
@@ -166,6 +166,7 @@ SurfaceSdlGraphicsManager::SurfaceSdlGraphicsManager(SdlEventSource *sdlEventSou
 
 SurfaceSdlGraphicsManager::~SurfaceSdlGraphicsManager() {
 	unloadGFXMode();
+	delete _scaler;
 	if (_mouseOrigSurface) {
 		SDL_FreeSurface(_mouseOrigSurface);
 		if (_mouseOrigSurface == _mouseSurface) {
@@ -581,7 +582,7 @@ bool SurfaceSdlGraphicsManager::setScaler(uint mode, int factor) {
 	else if (_scalerPlugins[mode]->get<ScalerPluginObject>().hasFactor(_oldVideoMode.scaleFactor))
 		newFactor = _oldVideoMode.scaleFactor;
 	else
-		newFactor = _scalerPlugins[mode]->get<ScalerPluginObject>().getFactor();
+		newFactor = _scalerPlugins[mode]->get<ScalerPluginObject>().getDefaultFactor();
 
 	if (_oldVideoMode.setup && _oldVideoMode.scaleFactor != newFactor)
 		_transactionDetails.needHotswap = true;
@@ -608,19 +609,18 @@ void SurfaceSdlGraphicsManager::setGraphicsModeIntern() {
 #endif
 		) {
 		Graphics::PixelFormat format = convertSDLPixelFormat(_hwScreen->format);
-		if (_scalerPlugin)
-			_scalerPlugin->deinitialize();
+		delete _scaler;
 
 		_scalerPlugin = &_scalerPlugins[_videoMode.scalerIndex]->get<ScalerPluginObject>();
-		_scalerPlugin->initialize(format);
+		_scaler = _scalerPlugin->createInstance(format);
 	}
 
-	_scalerPlugin->setFactor(_videoMode.scaleFactor);
+	_scaler->setFactor(_videoMode.scaleFactor);
 	_extraPixels = _scalerPlugin->extraPixels();
 	_useOldSrc = _scalerPlugin->useOldSource();
 	if (_useOldSrc) {
-		_scalerPlugin->enableSource(true);
-		_scalerPlugin->setSource((byte *)_tmpscreen->pixels, _tmpscreen->pitch,
+		_scaler->enableSource(true);
+		_scaler->setSource((byte *)_tmpscreen->pixels, _tmpscreen->pitch,
 									_videoMode.screenWidth, _videoMode.screenHeight, _maxExtraPixels);
 	}
 
@@ -637,6 +637,11 @@ uint SurfaceSdlGraphicsManager::getScaler() const {
 	return _videoMode.scalerIndex;
 }
 
+uint SurfaceSdlGraphicsManager::getScaleFactor() const {
+	assert(_transactionMode == kTransactionNone);
+	return _videoMode.scaleFactor;
+}
+
 #if SDL_VERSION_ATLEAST(2, 0, 0)
 const OSystem::GraphicsMode *SurfaceSdlGraphicsManager::getSupportedStretchModes() const {
 	return s_supportedStretchModes;
@@ -920,7 +925,7 @@ bool SurfaceSdlGraphicsManager::loadGFXMode() {
 
 	if (_useOldSrc) {
 		// Create surface containing previous frame's data to pass to scaler
-		_scalerPlugin->setSource((byte *)_tmpscreen->pixels, _tmpscreen->pitch,
+		_scaler->setSource((byte *)_tmpscreen->pixels, _tmpscreen->pitch,
 									_videoMode.screenWidth, _videoMode.screenHeight, _maxExtraPixels);
 	}
 
@@ -1124,11 +1129,11 @@ void SurfaceSdlGraphicsManager::internUpdateScreen() {
 		if (_needRestoreAfterOverlay) {
 			// This is needed for the Edge scaler which seems to be the only scaler to use the "_useOldSrc" feature.
 			// Otherwise the screen will not be properly restored after removing the overlay. We need to trigger a
-			// regeneration of SourceScaler::_bufferedOutput. The call to _scalerPlugin->setFactor() down below could
+			// regeneration of SourceScaler::_bufferedOutput. The call to _scaler->setFactor() down below could
 			// do that in theory, but it won't unless the factor actually changes (which it doesn't). Now, the code
 			// in SourceScaler::setSource() looks a bit fishy, e. g. the *src argument isn't even used. But otherwise
 			// it does what we want here at least...
-			_scalerPlugin->setSource(0, _tmpscreen->pitch, _videoMode.screenWidth, _videoMode.screenHeight, _maxExtraPixels);
+			_scaler->setSource(0, _tmpscreen->pitch, _videoMode.screenWidth, _videoMode.screenHeight, _maxExtraPixels);
 		}
 
 		origSurf = _screen;
@@ -1143,7 +1148,7 @@ void SurfaceSdlGraphicsManager::internUpdateScreen() {
 		width = _videoMode.overlayWidth;
 		height = _videoMode.overlayHeight;
 		scale1 = 1;
-		oldScaleFactor = _scalerPlugin->setFactor(1);
+		oldScaleFactor = _scaler->setFactor(1);
 		_needRestoreAfterOverlay = _useOldSrc;
 	}
 
@@ -1217,7 +1222,7 @@ void SurfaceSdlGraphicsManager::internUpdateScreen() {
 				if (_videoMode.aspectRatioCorrection && !_overlayVisible)
 					dst_y = real2Aspect(dst_y);
 
-				_scalerPlugin->scale((byte *)srcSurf->pixels + (r->x + _maxExtraPixels) * 2 + (r->y + _maxExtraPixels) * srcPitch, srcPitch,
+				_scaler->scale((byte *)srcSurf->pixels + (r->x + _maxExtraPixels) * 2 + (r->y + _maxExtraPixels) * srcPitch, srcPitch,
 					(byte *)_hwScreen->pixels + dst_x * 2 + dst_y * dstPitch, dstPitch, r->w, dst_h, r->x, r->y);
 			}
 
@@ -1333,7 +1338,7 @@ void SurfaceSdlGraphicsManager::internUpdateScreen() {
 	}
 
 	// Set up the old scale factor
-	_scalerPlugin->setFactor(oldScaleFactor);
+	_scaler->setFactor(oldScaleFactor);
 
 	_numDirtyRects = 0;
 	_forceRedraw = false;
@@ -1692,7 +1697,7 @@ void SurfaceSdlGraphicsManager::clearOverlay() {
 	SDL_LockSurface(_tmpscreen);
 	SDL_LockSurface(_overlayscreen);
 
-	_scalerPlugin->scale((byte *)(_tmpscreen->pixels) + _maxExtraPixels * _tmpscreen->pitch + _maxExtraPixels * 2, _tmpscreen->pitch,
+	_scaler->scale((byte *)(_tmpscreen->pixels) + _maxExtraPixels * _tmpscreen->pitch + _maxExtraPixels * 2, _tmpscreen->pitch,
 	(byte *)_overlayscreen->pixels, _overlayscreen->pitch, _videoMode.screenWidth, _videoMode.screenHeight, 0, 0);
 
 #ifdef USE_ASPECT
@@ -2062,7 +2067,7 @@ void SurfaceSdlGraphicsManager::blitCursor() {
 		// HACK: AdvMame4x requires a height of at least 4 pixels, so we
 		// fall back on the Normal scaler when a smaller cursor is supplied.
 		if (_scalerPlugin->canDrawCursor() && (uint)_mouseCurState.h >= _extraPixels) {
-			_scalerPlugin->scale(
+			_scaler->scale(
 					(byte *)_mouseOrigSurface->pixels + _mouseOrigSurface->pitch * _maxExtraPixels + _maxExtraPixels * _mouseOrigSurface->format->BytesPerPixel,
 					_mouseOrigSurface->pitch, (byte *)_mouseSurface->pixels, _mouseSurface->pitch,
 					_mouseCurState.w, _mouseCurState.h, 0, 0);
@@ -2393,7 +2398,7 @@ void SurfaceSdlGraphicsManager::handleScalerHotkeys(uint mode, int factor) {
 			"%S %s%d\n%d x %d -> %d x %d",
 			_("Active graphics filter:").c_str(),
 			newScalerName,
-			_scalerPlugin->getFactor(),
+			_scaler->getFactor(),
 			_videoMode.screenWidth, _videoMode.screenHeight,
 			_hwScreen->w, _hwScreen->h);
 		displayMessageOnOSD(message);
@@ -2488,11 +2493,11 @@ bool SurfaceSdlGraphicsManager::notifyEvent(const Common::Event &event) {
 #endif
 
 	case kActionIncreaseScaleFactor:
-		handleScalerHotkeys(_videoMode.scalerIndex, _scalerPlugin->increaseFactor());
+		handleScalerHotkeys(_videoMode.scalerIndex, _scaler->increaseFactor());
 		return true;
 
 	case kActionDecreaseScaleFactor:
-		handleScalerHotkeys(_videoMode.scalerIndex, _scalerPlugin->decreaseFactor());
+		handleScalerHotkeys(_videoMode.scalerIndex, _scaler->decreaseFactor());
 		return true;
 
 	case kActionNextScaleFilter: {
@@ -2501,7 +2506,7 @@ bool SurfaceSdlGraphicsManager::notifyEvent(const Common::Event &event) {
 			scalerIndex = 0;
 		}
 
-		handleScalerHotkeys(scalerIndex, _scalerPlugins[scalerIndex]->get<ScalerPluginObject>().getFactor());
+		handleScalerHotkeys(scalerIndex, _scaler->getFactor());
 		return true;
 	}
 
@@ -2512,7 +2517,7 @@ bool SurfaceSdlGraphicsManager::notifyEvent(const Common::Event &event) {
 		}
 		scalerIndex--;
 
-		handleScalerHotkeys(scalerIndex, _scalerPlugins[scalerIndex]->get<ScalerPluginObject>().getFactor());
+		handleScalerHotkeys(scalerIndex, _scaler->getFactor());
 		return true;
 	}
 
diff --git a/backends/graphics/surfacesdl/surfacesdl-graphics.h b/backends/graphics/surfacesdl/surfacesdl-graphics.h
index 34310369ab..5e68acdcd1 100644
--- a/backends/graphics/surfacesdl/surfacesdl-graphics.h
+++ b/backends/graphics/surfacesdl/surfacesdl-graphics.h
@@ -77,6 +77,7 @@ public:
 	virtual uint getDefaultScaleFactor() const override;
 	virtual bool setScaler(uint mode, int factor) override;
 	virtual uint getScaler() const override;
+	virtual uint getScaleFactor() const override;
 #ifdef USE_RGB_COLOR
 	virtual Graphics::PixelFormat getScreenFormat() const override { return _screenFormat; }
 	virtual Common::List<Graphics::PixelFormat> getSupportedFormats() const override;
@@ -325,6 +326,7 @@ protected:
 
 	const PluginList &_scalerPlugins;
 	ScalerPluginObject *_scalerPlugin;
+	Scaler *_scaler;
 	uint _maxExtraPixels;
 	uint _extraPixels;
 
diff --git a/backends/modular-backend.cpp b/backends/modular-backend.cpp
index 7950018d57..74b14d81f8 100644
--- a/backends/modular-backend.cpp
+++ b/backends/modular-backend.cpp
@@ -124,6 +124,10 @@ uint ModularGraphicsBackend::getScaler() const {
 	return _graphicsManager->getScaler();
 }
 
+uint ModularGraphicsBackend::getScaleFactor() const {
+	return _graphicsManager->getScaleFactor();
+}
+
 #ifdef USE_RGB_COLOR
 
 Graphics::PixelFormat ModularGraphicsBackend::getScreenFormat() const {
diff --git a/backends/modular-backend.h b/backends/modular-backend.h
index 2318bfc0eb..d0df5f629d 100644
--- a/backends/modular-backend.h
+++ b/backends/modular-backend.h
@@ -81,6 +81,7 @@ public:
 	using BaseBackend::setScaler;
 	virtual bool setScaler(uint mode, int factor) override final;
 	virtual uint getScaler() const override final;
+	virtual uint getScaleFactor() const override final;
 #ifdef USE_RGB_COLOR
 	virtual Graphics::PixelFormat getScreenFormat() const override final;
 	virtual Common::List<Graphics::PixelFormat> getSupportedFormats() const override final;
diff --git a/common/system.h b/common/system.h
index bbbf4378a8..a80ce9bf51 100644
--- a/common/system.h
+++ b/common/system.h
@@ -907,12 +907,19 @@ public:
 	virtual bool setScaler(const char *name, int factor) { return false; }
 
 	/**
-	 * Determine which stretch mode is currently active.
+	 * Determine which scaler is currently active.
 	 *
 	 * @return ID of the active stretch mode.
 	 */
 	virtual uint getScaler() const { return 0; }
 
+	/**
+	 * Determine which scale factor is currently active.
+	 *
+	 * @return The active scale factor.
+	 */
+	virtual uint getScaleFactor() const { return 1; }
+
 
 	/**
 	 * Set the size and color format of the virtual screen.
diff --git a/graphics/scaler/dotmatrix.cpp b/graphics/scaler/dotmatrix.cpp
index 6e5931dfa3..38a13efdab 100644
--- a/graphics/scaler/dotmatrix.cpp
+++ b/graphics/scaler/dotmatrix.cpp
@@ -22,13 +22,8 @@
 #include "graphics/scaler/dotmatrix.h"
 #include "graphics/scaler.h"
 
-DotMatrixPlugin::DotMatrixPlugin() {
+DotMatrixScaler::DotMatrixScaler(const Graphics::PixelFormat &format) : Scaler(format) {
 	_factor = 2;
-	_factors.push_back(2);
-}
-
-void DotMatrixPlugin::initialize(const Graphics::PixelFormat &format) {
-	ScalerPluginObject::initialize(format);
 
 	if (format.bytesPerPixel == 2) {
 		uint16 *lookup16 = (uint16 *)lookup;
@@ -53,7 +48,7 @@ void DotMatrixPlugin::initialize(const Graphics::PixelFormat &format) {
 	}
 }
 
-void DotMatrixPlugin::scaleIntern(const uint8 *srcPtr, uint32 srcPitch,
+void DotMatrixScaler::scaleIntern(const uint8 *srcPtr, uint32 srcPitch,
 							uint8 *dstPtr, uint32 dstPitch, int width, int height, int x, int y) {
 	if (_format.bytesPerPixel == 2) {
 		scaleIntern<uint16>(srcPtr, srcPitch, dstPtr, dstPitch, width, height, x, y);
@@ -62,29 +57,21 @@ void DotMatrixPlugin::scaleIntern(const uint8 *srcPtr, uint32 srcPitch,
 	}
 }
 
-uint DotMatrixPlugin::increaseFactor() {
+uint DotMatrixScaler::increaseFactor() {
 	return _factor;
 }
 
-uint DotMatrixPlugin::decreaseFactor() {
+uint DotMatrixScaler::decreaseFactor() {
 	return _factor;
 }
 
-const char *DotMatrixPlugin::getName() const {
-	return "dotmatrix";
-}
-
-const char *DotMatrixPlugin::getPrettyName() const {
-	return "DotMatrix";
-}
-
 template<typename Pixel>
 static inline Pixel DOT(const Pixel *dotmatrix, Pixel c, int j, int i) {
 	return c - ((c >> 2) & dotmatrix[((j & 3) << 2) + (i & 3)]);
 }
 
 template<typename Pixel>
-void DotMatrixPlugin::scaleIntern(const uint8 *srcPtr, uint32 srcPitch, uint8 *dstPtr, uint32 dstPitch,
+void DotMatrixScaler::scaleIntern(const uint8 *srcPtr, uint32 srcPitch, uint8 *dstPtr, uint32 dstPitch,
 					int width, int height, int x, int y) {
 
 	const Pixel *dotmatrix = (Pixel *)lookup;
@@ -111,4 +98,33 @@ void DotMatrixPlugin::scaleIntern(const uint8 *srcPtr, uint32 srcPitch, uint8 *d
 	}
 }
 
+
+class DotMatrixPlugin final : public ScalerPluginObject {
+public:
+	DotMatrixPlugin();
+
+	virtual Scaler *createInstance(const Graphics::PixelFormat &format) const override;
+
+	virtual bool canDrawCursor() const override { return false; }
+	virtual uint extraPixels() const override { return 0; }
+	virtual const char *getName() const override;
+	virtual const char *getPrettyName() const override;
+};
+
+DotMatrixPlugin::DotMatrixPlugin() {
+	_factors.push_back(2);
+}
+
+Scaler *DotMatrixPlugin::createInstance(const Graphics::PixelFormat &format) const {
+	return new DotMatrixScaler(format);
+}
+
+const char *DotMatrixPlugin::getName() const {
+	return "dotmatrix";
+}
+
+const char *DotMatrixPlugin::getPrettyName() const {
+	return "DotMatrix";
+}
+
 REGISTER_PLUGIN_STATIC(DOTMATRIX, PLUGIN_TYPE_SCALER, DotMatrixPlugin);
diff --git a/graphics/scaler/dotmatrix.h b/graphics/scaler/dotmatrix.h
index dbdf62bfe5..db0e4bd5e7 100644
--- a/graphics/scaler/dotmatrix.h
+++ b/graphics/scaler/dotmatrix.h
@@ -24,16 +24,11 @@
 
 #include "graphics/scalerplugin.h"
 
-class DotMatrixPlugin : public ScalerPluginObject {
+class DotMatrixScaler : public Scaler {
 public:
-	DotMatrixPlugin();
-	virtual void initialize(const Graphics::PixelFormat &format) override;
+	DotMatrixScaler(const Graphics::PixelFormat &format);
 	virtual uint increaseFactor() override;
 	virtual uint decreaseFactor() override;
-	virtual bool canDrawCursor() const override { return false; }
-	virtual uint extraPixels() const override { return 0; }
-	virtual const char *getName() const override;
-	virtual const char *getPrettyName() const override;
 protected:
 	virtual void scaleIntern(const uint8 *srcPtr, uint32 srcPitch,
 							uint8 *dstPtr, uint32 dstPitch, int width, int height, int x, int y) override;
diff --git a/graphics/scaler/edge.cpp b/graphics/scaler/edge.cpp
index 9cfff79654..cb97ea301d 100644
--- a/graphics/scaler/edge.cpp
+++ b/graphics/scaler/edge.cpp
@@ -307,7 +307,7 @@ uint16 convertTo16Bit(const typename ColorMask::PixelType p) {
 
 
 template<typename ColorMask>
-int16 *EdgePlugin::chooseGreyscale(typename ColorMask::PixelType *pixels) {
+int16 *EdgeScaler::chooseGreyscale(typename ColorMask::PixelType *pixels) {
 	int i, j;
 	int32 scores[3];
 
@@ -379,7 +379,7 @@ int16 *EdgePlugin::chooseGreyscale(typename ColorMask::PixelType *pixels) {
 
 
 template<typename ColorMask>
-int32 EdgePlugin::calcPixelDiffNosqrt(typename ColorMask::PixelType pixel1, typename ColorMask::PixelType pixel2) {
+int32 EdgeScaler::calcPixelDiffNosqrt(typename ColorMask::PixelType pixel1, typename ColorMask::PixelType pixel2) {
 	pixel1 = convertTo16Bit<ColorMask>(pixel1);
 	pixel2 = convertTo16Bit<ColorMask>(pixel2);
 
@@ -452,7 +452,7 @@ int32 EdgePlugin::calcPixelDiffNosqrt(typename ColorMask::PixelType pixel1, type
 }
 
 
-int EdgePlugin::findPrincipleAxis(int16 *diffs, int16 *bplane,
+int EdgeScaler::findPrincipleAxis(int16 *diffs, int16 *bplane,
 								  int8 *sim,
 								  int32 *return_angle) {
 	struct xy_point {
@@ -677,7 +677,7 @@ int EdgePlugin::findPrincipleAxis(int16 *diffs, int16 *bplane,
 
 
 template<typename Pixel>
-int EdgePlugin::checkArrows(int best_dir, Pixel *pixels, int8 *sim, int half_flag) {
+int EdgeScaler::checkArrows(int best_dir, Pixel *pixels, int8 *sim, int half_flag) {
 	Pixel center = pixels[4];
 
 	if (center == pixels[0] && center == pixels[2] &&
@@ -784,7 +784,7 @@ int EdgePlugin::checkArrows(int best_dir, Pixel *pixels, int8 *sim, int half_fla
 
 
 template<typename Pixel>
-int EdgePlugin::refineDirection(char edge_type, Pixel *pixels, int16 *bptr,
+int EdgeScaler::refineDirection(char edge_type, Pixel *pixels, int16 *bptr,
 								int8 *sim, double angle) {
 	int32 sums_dir[9] = { 0 };
 	int32 sum;
@@ -1625,7 +1625,7 @@ int EdgePlugin::refineDirection(char edge_type, Pixel *pixels, int16 *bptr,
 
 
 template<typename Pixel>
-int EdgePlugin::fixKnights(int sub_type, Pixel *pixels, int8 *sim) {
+int EdgeScaler::fixKnights(int sub_type, Pixel *pixels, int8 *sim) {
 	Pixel center = pixels[4];
 	int dir = sub_type;
 	int n = 0;
@@ -1772,7 +1772,7 @@ int EdgePlugin::fixKnights(int sub_type, Pixel *pixels, int8 *sim) {
 #define greenMask   0x07E0
 
 template<typename ColorMask>
-void EdgePlugin::antiAliasGridClean3x(uint8 *dptr, int dstPitch,
+void EdgeScaler::antiAliasGridClean3x(uint8 *dptr, int dstPitch,
 		typename ColorMask::PixelType *pixels, int sub_type, int16 *bptr) {
 	typedef typename ColorMask::PixelType Pixel;
 
@@ -2373,7 +2373,7 @@ void EdgePlugin::antiAliasGridClean3x(uint8 *dptr, int dstPitch,
 
 
 template<typename ColorMask>
-void EdgePlugin::antiAliasGrid2x(uint8 *dptr, int dstPitch,
+void EdgeScaler::antiAliasGrid2x(uint8 *dptr, int dstPitch,
 									typename ColorMask::PixelType *pixels, int sub_type, int16 *bptr,
 									int8 *sim,
 									int interpolate_2x) {
@@ -3309,7 +3309,7 @@ void drawUnchangedGrid2x(byte *dptr, int dstPitch,
 
 
 template<typename ColorMask>
-void EdgePlugin::antiAliasPass3x(const uint8 *src, uint8 *dst,
+void EdgeScaler::antiAliasPass3x(const uint8 *src, uint8 *dst,
 								 int w, int h,
 								 int srcPitch, int dstPitch,
 								 bool haveOldSrc,
@@ -3393,7 +3393,7 @@ void EdgePlugin::antiAliasPass3x(const uint8 *src, uint8 *dst,
 
 
 template<typename ColorMask>
-void EdgePlugin::antiAliasPass2x(const uint8 *src, uint8 *dst,
+void EdgeScaler::antiAliasPass2x(const uint8 *src, uint8 *dst,
 								 int w, int h,
 								 int srcPitch, int dstPitch,
 								 int interpolate_2x,
@@ -3478,7 +3478,7 @@ void EdgePlugin::antiAliasPass2x(const uint8 *src, uint8 *dst,
 }
 
 
-void EdgePlugin::initTables(const uint8 *srcPtr, uint32 srcPitch,
+void EdgeScaler::initTables(const uint8 *srcPtr, uint32 srcPitch,
 							int width, int height) {
 	double r_float, g_float, b_float;
 	int r, g, b;
@@ -3531,19 +3531,14 @@ void EdgePlugin::initTables(const uint8 *srcPtr, uint32 srcPitch,
 	}
 }
 
-EdgePlugin::EdgePlugin() : SourceScaler() {
+EdgeScaler::EdgeScaler(const Graphics::PixelFormat &format) : SourceScaler(format) {
 	_factor = 2;
-	_factors.push_back(2);
-	_factors.push_back(3);
-}
 
-void EdgePlugin::initialize(const Graphics::PixelFormat &format) {
-	SourceScaler::initialize(format);
 	initTables(0, 0, 0, 0);
 }
 
 #if 0
-void EdgePlugin::scale(const uint8 *srcPtr, uint32 srcPitch,
+void EdgeScaler::scale(const uint8 *srcPtr, uint32 srcPitch,
 					   uint8 *dstPtr, uint32 dstPitch, int width, int height, int x, int y) {
 	if (_format.bytesPerPixel == 2) {
 		if (_factor == 2) {
@@ -3573,7 +3568,7 @@ void EdgePlugin::scale(const uint8 *srcPtr, uint32 srcPitch,
 }
 #endif
 
-void EdgePlugin::internScale(const uint8 *srcPtr, uint32 srcPitch,
+void EdgeScaler::internScale(const uint8 *srcPtr, uint32 srcPitch,
 					   uint8 *dstPtr, uint32 dstPitch, const uint8 *oldSrcPtr, uint32 oldSrcPitch, int width, int height, const uint8 *buffer, uint32 bufferPitch) {
 	bool enable = oldSrcPtr != NULL;
 	if (_format.bytesPerPixel == 2) {
@@ -3603,18 +3598,41 @@ void EdgePlugin::internScale(const uint8 *srcPtr, uint32 srcPitch,
 	}
 }
 
-uint EdgePlugin::increaseFactor() {
+uint EdgeScaler::increaseFactor() {
 	if (_factor == 2)
 		setFactor(_factor + 1);
 	return _factor;
 }
 
-uint EdgePlugin::decreaseFactor() {
+uint EdgeScaler::decreaseFactor() {
 	if (_factor == 3)
 		setFactor(_factor - 1);
 	return _factor;
 }
 
+
+class EdgePlugin final : public ScalerPluginObject {
+public:
+	EdgePlugin();
+
+	virtual Scaler *createInstance(const Graphics::PixelFormat &format) const override;
+
+	virtual bool canDrawCursor() const override { return false; }
+	virtual bool useOldSource() const override { return true; }
+	virtual uint extraPixels() const override { return 1; }
+	virtual const char *getName() const override;
+	virtual const char *getPrettyName() const override;
+};
+
+EdgePlugin::EdgePlugin() {
+	_factors.push_back(2);
+	_factors.push_back(3);
+}
+
+Scaler *EdgePlugin::createInstance(const Graphics::PixelFormat &format) const {
+	return new EdgeScaler(format);
+}
+
 const char *EdgePlugin::getName() const {
 	return "edge";
 }
diff --git a/graphics/scaler/edge.h b/graphics/scaler/edge.h
index 69e4a5d9ea..2e439e603b 100644
--- a/graphics/scaler/edge.h
+++ b/graphics/scaler/edge.h
@@ -24,18 +24,12 @@
 
 #include "graphics/scalerplugin.h"
 
-class EdgePlugin : public SourceScaler {
+class EdgeScaler : public SourceScaler {
 public:
 
-	EdgePlugin();
-	virtual void initialize(const Graphics::PixelFormat &format) override;
+	EdgeScaler(const Graphics::PixelFormat &format);
 	virtual uint increaseFactor() override;
 	virtual uint decreaseFactor() override;
-	virtual bool canDrawCursor() const override { return false; }
-	virtual bool useOldSource() const override { return true; }
-	virtual uint extraPixels() const override { return 1; }
-	virtual const char *getName() const override;
-	virtual const char *getPrettyName() const override;
 
 protected:
 
diff --git a/graphics/scaler/hq.cpp b/graphics/scaler/hq.cpp
index 14cf2213ee..4023884f00 100644
--- a/graphics/scaler/hq.cpp
+++ b/graphics/scaler/hq.cpp
@@ -4988,14 +4988,8 @@ static void HQ3x_implementation(const uint8 *srcPtr, uint32 srcPitch, uint8 *dst
 	}
 }
 
-HQPlugin::HQPlugin() {
+HQScaler::HQScaler(const Graphics::PixelFormat &format) : Scaler(format) {
 	_factor = 2;
-	_factors.push_back(2);
-	_factors.push_back(3);
-}
-
-void HQPlugin::initialize(const Graphics::PixelFormat &format) {
-	ScalerPluginObject::initialize(format);
 
 	if (format.bytesPerPixel == 2) {
 		InitLUT(format);
@@ -5008,12 +5002,12 @@ void HQPlugin::initialize(const Graphics::PixelFormat &format) {
 	}
 }
 
-void HQPlugin::deinitialize() {
+HQScaler::~HQScaler() {
 	free(RGBtoYUV);
 	RGBtoYUV = 0;
 }
 
-void HQPlugin::scaleIntern(const uint8 *srcPtr, uint32 srcPitch,
+void HQScaler::scaleIntern(const uint8 *srcPtr, uint32 srcPitch,
 							uint8 *dstPtr, uint32 dstPitch, int width, int height, int x, int y) {
 	if (_format.bytesPerPixel == 2) {
 		switch (_factor) {
@@ -5065,18 +5059,40 @@ void HQPlugin::scaleIntern(const uint8 *srcPtr, uint32 srcPitch,
 	}
 }
 
-uint HQPlugin::increaseFactor() {
+uint HQScaler::increaseFactor() {
 	if (_factor < 3)
 		setFactor(_factor + 1);
 	return _factor;
 }
 
-uint HQPlugin::decreaseFactor() {
+uint HQScaler::decreaseFactor() {
 	if (_factor > 2)
 		setFactor(_factor - 1);
 	return _factor;
 }
 
+
+class HQPlugin final : public ScalerPluginObject {
+public:
+	HQPlugin();
+
+	virtual Scaler *createInstance(const Graphics::PixelFormat &format) const override;
+
+	virtual bool canDrawCursor() const override { return false; }
+	virtual uint extraPixels() const override { return 1; }
+	virtual const char *getName() const override;
+	virtual const char *getPrettyName() const override;
+};
+
+HQPlugin::HQPlugin() {
+	_factors.push_back(2);
+	_factors.push_back(3);
+}
+
+Scaler *HQPlugin::createInstance(const Graphics::PixelFormat &format) const {
+	return new HQScaler(format);
+}
+
 const char *HQPlugin::getName() const {
 	return "hq";
 }
diff --git a/graphics/scaler/hq.h b/graphics/scaler/hq.h
index 563bdb3d44..3a2878d861 100644
--- a/graphics/scaler/hq.h
+++ b/graphics/scaler/hq.h
@@ -24,17 +24,12 @@
 
 #include "graphics/scalerplugin.h"
 
-class HQPlugin : public ScalerPluginObject {
+class HQScaler : public Scaler {
 public:
-	HQPlugin();
-	virtual void initialize(const Graphics::PixelFormat &format) override;
-	virtual void deinitialize() override;
+	HQScaler(const Graphics::PixelFormat &format);
+	~HQScaler();
 	virtual uint increaseFactor() override;
 	virtual uint decreaseFactor() override;
-	virtual bool canDrawCursor() const override { return false; }
-	virtual uint extraPixels() const override { return 1; }
-	virtual const char *getName() const override;
-	virtual const char *getPrettyName() const override;
 protected:
 	virtual void scaleIntern(const uint8 *srcPtr, uint32 srcPitch,
 							uint8 *dstPtr, uint32 dstPitch, int width, int height, int x, int y) override;
diff --git a/graphics/scaler/normal.cpp b/graphics/scaler/normal.cpp
index 140f6c52cb..4f41ec37d1 100644
--- a/graphics/scaler/normal.cpp
+++ b/graphics/scaler/normal.cpp
@@ -21,17 +21,6 @@
 
 #include "graphics/scaler/normal.h"
 
-NormalPlugin::NormalPlugin() {
-	_factor = 1;
-	_factors.push_back(1);
-#ifdef USE_SCALERS
-	_factors.push_back(2);
-	_factors.push_back(3);
-	_factors.push_back(4);
-	_factors.push_back(5);
-#endif
-}
-
 #ifdef USE_SCALERS
 
 /**
@@ -217,7 +206,7 @@ void Normal5x(const uint8 *srcPtr, uint32 srcPitch, uint8 *dstPtr, uint32 dstPit
 }
 #endif
 
-void NormalPlugin::scaleIntern(const uint8 *srcPtr, uint32 srcPitch,
+void NormalScaler::scaleIntern(const uint8 *srcPtr, uint32 srcPitch,
 							uint8 *dstPtr, uint32 dstPitch, int width, int height, int x, int y) {
 #ifdef USE_SCALERS
 	if (_format.bytesPerPixel == 2) {
@@ -259,7 +248,7 @@ void NormalPlugin::scaleIntern(const uint8 *srcPtr, uint32 srcPitch,
 #endif
 }
 
-uint NormalPlugin::increaseFactor() {
+uint NormalScaler::increaseFactor() {
 #ifdef USE_SCALERS
 	if (_factor < 5)
 		setFactor(_factor + 1);
@@ -267,7 +256,7 @@ uint NormalPlugin::increaseFactor() {
 	return _factor;
 }
 
-uint NormalPlugin::decreaseFactor() {
+uint NormalScaler::decreaseFactor() {
 #ifdef USE_SCALERS
 	if (_factor > 1)
 		setFactor(_factor - 1);
@@ -275,6 +264,33 @@ uint NormalPlugin::decreaseFactor() {
 	return _factor;
 }
 
+
+class NormalPlugin final : public ScalerPluginObject {
+public:
+	NormalPlugin();
+
+	virtual Scaler *createInstance(const Graphics::PixelFormat &format) const override;
+
+	virtual bool canDrawCursor() const override { return true; }
+	virtual uint extraPixels() const override { return 0; }
+	virtual const char *getName() const override;
+	virtual const char *getPrettyName() const override;
+};
+
+NormalPlugin::NormalPlugin() {
+	_factors.push_back(1);
+#ifdef USE_SCALERS
+	_factors.push_back(2);
+	_factors.push_back(3);
+	_factors.push_back(4);
+	_factors.push_back(5);
+#endif
+}
+
+Scaler *NormalPlugin::createInstance(const Graphics::PixelFormat &format) const {
+	return new NormalScaler(format);
+}
+
 const char *NormalPlugin::getName() const {
 	return "normal";
 }
diff --git a/graphics/scaler/normal.h b/graphics/scaler/normal.h
index a4a4e38cc7..7cf5920c50 100644
--- a/graphics/scaler/normal.h
+++ b/graphics/scaler/normal.h
@@ -24,15 +24,11 @@
 
 #include "graphics/scalerplugin.h"
 
-class NormalPlugin : public ScalerPluginObject {
+class NormalScaler : public Scaler {
 public:
-	NormalPlugin();
+	NormalScaler(const Graphics::PixelFormat &format) : Scaler(format) { _factor = 1; }
 	virtual uint increaseFactor() override;
 	virtual uint decreaseFactor() override;
-	virtual bool canDrawCursor() const override { return true; }
-	virtual uint extraPixels() const override { return 0; }
-	virtual const char *getName() const override;
-	virtual const char *getPrettyName() const override;
 protected:
 	virtual void scaleIntern(const uint8 *srcPtr, uint32 srcPitch,
 							uint8 *dstPtr, uint32 dstPitch, int width, int height, int x, int y) override;
diff --git a/graphics/scaler/pm.cpp b/graphics/scaler/pm.cpp
index 2ffabff34c..57bf4918e4 100644
--- a/graphics/scaler/pm.cpp
+++ b/graphics/scaler/pm.cpp
@@ -186,13 +186,7 @@ void scaleIntern(const uint8 *srcPtr, uint32 srcPitch, uint8 *dstPtr, uint32 dst
 
 }
 
-
-PMPlugin::PMPlugin() {
-	_factor = 2;
-	_factors.push_back(2);
-}
-
-void PMPlugin::scaleIntern(const uint8 *srcPtr, uint32 srcPitch,
+void PMScaler::scaleIntern(const uint8 *srcPtr, uint32 srcPitch,
 							uint8 *dstPtr, uint32 dstPitch, int width, int height, int x, int y) {
 	if (_format.bytesPerPixel == 2) {
 		if (_format.gLoss == 2)
@@ -207,14 +201,36 @@ void PMPlugin::scaleIntern(const uint8 *srcPtr, uint32 srcPitch,
 	}
 }
 
-uint PMPlugin::increaseFactor() {
+uint PMScaler::increaseFactor() {
 	return _factor;
 }
 
-uint PMPlugin::decreaseFactor() {
+uint PMScaler::decreaseFactor() {
 	return _factor;
 }
 
+
+class PMPlugin final : public ScalerPluginObject {
+public:
+	PMPlugin();
+
+	virtual Scaler *createInstance(const Graphics::PixelFormat &format) const override;
+
+	virtual bool canDrawCursor() const override { return false; }
+	virtual uint extraPixels() const override { return 1; }
+	virtual const char *getName() const override;
+	virtual const char *getPrettyName() const override;
+};
+
+
+PMPlugin::PMPlugin() {
+	_factors.push_back(2);
+}
+
+Scaler *PMPlugin::createInstance(const Graphics::PixelFormat &format) const {
+	return new PMScaler(format);
+}
+
 const char *PMPlugin::getName() const {
 	return "pm";
 }
diff --git a/graphics/scaler/pm.h b/graphics/scaler/pm.h
index d24dd42bd9..86769a407a 100644
--- a/graphics/scaler/pm.h
+++ b/graphics/scaler/pm.h
@@ -24,17 +24,14 @@
 
 #include "graphics/scalerplugin.h"
 
-class PMPlugin : public ScalerPluginObject {
+class PMScaler : public Scaler {
 public:
-	PMPlugin();
-	virtual void scaleIntern(const uint8 *srcPtr, uint32 srcPitch,
-							uint8 *dstPtr, uint32 dstPitch, int width, int height, int x, int y) override;
+	PMScaler(const Graphics::PixelFormat &format) : Scaler(format) { _factor = 2; }
 	virtual uint increaseFactor() override;
 	virtual uint decreaseFactor() override;
-	virtual bool canDrawCursor() const override { return false; }
-	virtual uint extraPixels() const override { return 1; }
-	virtual const char *getName() const override;
-	virtual const char *getPrettyName() const override;
+protected:
+	virtual void scaleIntern(const uint8 *srcPtr, uint32 srcPitch,
+							uint8 *dstPtr, uint32 dstPitch, int width, int height, int x, int y) override;
 };
 
 #endif
diff --git a/graphics/scaler/sai.cpp b/graphics/scaler/sai.cpp
index a3b881422b..3c58b69ee1 100644
--- a/graphics/scaler/sai.cpp
+++ b/graphics/scaler/sai.cpp
@@ -392,12 +392,7 @@ void _2xSaITemplate(const uint8 *srcPtr, uint32 srcPitch, uint8 *dstPtr, uint32
 
 // SAI
 
-SAIPlugin::SAIPlugin() {
-	_factor = 2;
-	_factors.push_back(2);
-}
-
-void SAIPlugin::scaleIntern(const uint8 *srcPtr, uint32 srcPitch,
+void SAIScaler::scaleIntern(const uint8 *srcPtr, uint32 srcPitch,
 							uint8 *dstPtr, uint32 dstPitch, int width, int height, int x, int y) {
 	if (_format.bytesPerPixel == 2) {
 		if (_format.gLoss == 2)
@@ -412,14 +407,35 @@ void SAIPlugin::scaleIntern(const uint8 *srcPtr, uint32 srcPitch,
 	}
 }
 
-uint SAIPlugin::increaseFactor() {
+uint SAIScaler::increaseFactor() {
 	return _factor;
 }
 
-uint SAIPlugin::decreaseFactor() {
+uint SAIScaler::decreaseFactor() {
 	return _factor;
 }
 
+
+class SAIPlugin final : public ScalerPluginObject {
+public:
+	SAIPlugin();
+
+	virtual Scaler *createInstance(const Graphics::PixelFormat &format) const override;
+
+	virtual bool canDrawCursor() const override { return false; }
+	virtual uint extraPixels() const override { return 2; }
+	virtual const char *getName() const override;
+	virtual const char *getPrettyName() const override;
+};
+
+SAIPlugin::SAIPlugin() {
+	_factors.push_back(2);
+}
+
+Scaler *SAIPlugin::createInstance(const Graphics::PixelFormat &format) const {
+	return new SAIScaler(format);
+}
+
 const char *SAIPlugin::getName() const {
 	return "sai";
 }
@@ -432,13 +448,7 @@ REGISTER_PLUGIN_STATIC(SAI, PLUGIN_TYPE_SCALER, SAIPlugin);
 
 // SuperSAI
 
-SuperSAIPlugin::SuperSAIPlugin() {
-	_factor = 2;
-	_factors.push_back(2);
-}
-
-
-void SuperSAIPlugin::scaleIntern(const uint8 *srcPtr, uint32 srcPitch,
+void SuperSAIScaler::scaleIntern(const uint8 *srcPtr, uint32 srcPitch,
 							uint8 *dstPtr, uint32 dstPitch, int width, int height, int x, int y) {
 	if (_format.bytesPerPixel == 2) {
 		if (_format.gLoss == 2)
@@ -453,14 +463,35 @@ void SuperSAIPlugin::scaleIntern(const uint8 *srcPtr, uint32 srcPitch,
 	}
 }
 
-uint SuperSAIPlugin::increaseFactor() {
+uint SuperSAIScaler::increaseFactor() {
 	return _factor;
 }
 
-uint SuperSAIPlugin::decreaseFactor() {
+uint SuperSAIScaler::decreaseFactor() {
 	return _factor;
 }
 
+
+class SuperSAIPlugin final : public ScalerPluginObject {
+public:
+	SuperSAIPlugin();
+
+	virtual Scaler *createInstance(const Graphics::PixelFormat &format) const override;
+
+	virtual bool canDrawCursor() const override { return false; }
+	virtual uint extraPixels() const override { return 2; }
+	virtual const char *getName() const override;
+	virtual const char *getPrettyName() const override;
+};
+
+SuperSAIPlugin::SuperSAIPlugin() {
+	_factors.push_back(2);
+}
+
+Scaler *SuperSAIPlugin::createInstance(const Graphics::PixelFormat &format) const {
+	return new SuperSAIScaler(format);
+}
+
 const char *SuperSAIPlugin::getName() const {
 	return "supersai";
 }
@@ -473,12 +504,7 @@ REGISTER_PLUGIN_STATIC(SUPERSAI, PLUGIN_TYPE_SCALER, SuperSAIPlugin);
 
 // SuperEagle
 
-SuperEaglePlugin::SuperEaglePlugin() {
-	_factor = 2;
-	_factors.push_back(2);
-}
-
-void SuperEaglePlugin::scaleIntern(const uint8 *srcPtr, uint32 srcPitch,
+void SuperEagleScaler::scaleIntern(const uint8 *srcPtr, uint32 srcPitch,
 							uint8 *dstPtr, uint32 dstPitch, int width, int height, int x, int y) {
 	if (_format.bytesPerPixel == 2) {
 		if (_format.gLoss == 2)
@@ -493,14 +519,35 @@ void SuperEaglePlugin::scaleIntern(const uint8 *srcPtr, uint32 srcPitch,
 	}
 }
 
-uint SuperEaglePlugin::increaseFactor() {
+uint SuperEagleScaler::increaseFactor() {
 	return _factor;
 }
 
-uint SuperEaglePlugin::decreaseFactor() {
+uint SuperEagleScaler::decreaseFactor() {
 	return _factor;
 }
 
+
+class SuperEaglePlugin final : public ScalerPluginObject {
+public:
+	SuperEaglePlugin();
+
+	virtual Scaler *createInstance(const Graphics::PixelFormat &format) const override;
+
+	virtual bool canDrawCursor() const override { return false; }
+	virtual uint extraPixels() const override { return 2; }
+	virtual const char *getName() const override;
+	virtual const char *getPrettyName() const override;
+};
+
+SuperEaglePlugin::SuperEaglePlugin() {
+	_factors.push_back(2);
+}
+
+Scaler *SuperEaglePlugin::createInstance(const Graphics::PixelFormat &format) const {
+	return new SuperEagleScaler(format);
+}
+
 const char *SuperEaglePlugin::getName() const {
 	return "supereagle";
 }
diff --git a/graphics/scaler/sai.h b/graphics/scaler/sai.h
index c509b5842d..1cf59bbcea 100644
--- a/graphics/scaler/sai.h
+++ b/graphics/scaler/sai.h
@@ -24,43 +24,31 @@
 
 #include "graphics/scalerplugin.h"
 
-class SAIPlugin : public ScalerPluginObject {
+class SAIScaler : public Scaler {
 public:
-	SAIPlugin();
+	SAIScaler(const Graphics::PixelFormat &format) : Scaler(format) { _factor = 2; }
 	virtual uint increaseFactor() override;
 	virtual uint decreaseFactor() override;
-	virtual bool canDrawCursor() const override { return false; }
-	virtual uint extraPixels() const override { return 2; }
-	virtual const char *getName() const override;
-	virtual const char *getPrettyName() const override;
 protected:
 	virtual void scaleIntern(const uint8 *srcPtr, uint32 srcPitch,
 							uint8 *dstPtr, uint32 dstPitch, int width, int height, int x, int y) override;
 };
 
-class SuperSAIPlugin : public ScalerPluginObject {
+class SuperSAIScaler : public Scaler {
 public:
-	SuperSAIPlugin();
+	SuperSAIScaler(const Graphics::PixelFormat &format) : Scaler(format) { _factor = 2; }
 	virtual uint increaseFactor() override;
 	virtual uint decreaseFactor() override;
-	virtual bool canDrawCursor() const override { return false; }
-	virtual uint extraPixels() const override { return 2; }
-	virtual const char *getName() const override;
-	virtual const char *getPrettyName() const override;
 protected:
 	virtual void scaleIntern(const uint8 *srcPtr, uint32 srcPitch,
 							uint8 *dstPtr, uint32 dstPitch, int width, int height, int x, int y) override;
 };
 
-class SuperEaglePlugin : public ScalerPluginObject {
+class SuperEagleScaler : public Scaler {
 public:
-	SuperEaglePlugin();
+	SuperEagleScaler(const Graphics::PixelFormat &format) : Scaler(format) { _factor = 2; }
 	virtual uint increaseFactor() override;
 	virtual uint decreaseFactor() override;
-	virtual bool canDrawCursor() const override { return false; }
-	virtual uint extraPixels() const override { return 2; }
-	virtual const char *getName() const override;
-	virtual const char *getPrettyName() const override;
 protected:
 	virtual void scaleIntern(const uint8 *srcPtr, uint32 srcPitch,
 							uint8 *dstPtr, uint32 dstPitch, int width, int height, int x, int y) override;
diff --git a/graphics/scaler/scalebit.cpp b/graphics/scaler/scalebit.cpp
index eecceef6e8..b5667ee3a0 100644
--- a/graphics/scaler/scalebit.cpp
+++ b/graphics/scaler/scalebit.cpp
@@ -352,14 +352,7 @@ void scale(unsigned scale, void* void_dst, unsigned dst_slice, const void* void_
 	}
 }
 
-AdvMamePlugin::AdvMamePlugin() {
-	_factor = 2;
-	_factors.push_back(2);
-	_factors.push_back(3);
-	_factors.push_back(4);
-}
-
-void AdvMamePlugin::scaleIntern(const uint8 *srcPtr, uint32 srcPitch,
+void AdvMameScaler::scaleIntern(const uint8 *srcPtr, uint32 srcPitch,
 							uint8 *dstPtr, uint32 dstPitch, int width, int height, int x, int y) {
 	if (_factor != 4)
 		::scale(_factor, dstPtr, dstPitch, srcPtr - srcPitch, srcPitch, _format.bytesPerPixel, width, height);
@@ -367,18 +360,41 @@ void AdvMamePlugin::scaleIntern(const uint8 *srcPtr, uint32 srcPitch,
 		::scale(_factor, dstPtr, dstPitch, srcPtr - srcPitch * 2, srcPitch, _format.bytesPerPixel, width, height);
 }
 
-uint AdvMamePlugin::increaseFactor() {
+uint AdvMameScaler::increaseFactor() {
 	if (_factor < 4)
 		setFactor(_factor + 1);
 	return _factor;
 }
 
-uint AdvMamePlugin::decreaseFactor() {
+uint AdvMameScaler::decreaseFactor() {
 	if (_factor > 2)
 		setFactor(_factor - 1);
 	return _factor;
 }
 
+
+class AdvMamePlugin final : public ScalerPluginObject {
+public:
+	AdvMamePlugin();
+
+	virtual Scaler *createInstance(const Graphics::PixelFormat &format) const override;
+
+	virtual bool canDrawCursor() const override { return true; }
+	virtual uint extraPixels() const override { return 4; }
+	virtual const char *getName() const override;
+	virtual const char *getPrettyName() const override;
+};
+
+AdvMamePlugin::AdvMamePlugin() {
+	_factors.push_back(2);
+	_factors.push_back(3);
+	_factors.push_back(4);
+}
+
+Scaler *AdvMamePlugin::createInstance(const Graphics::PixelFormat &format) const {
+	return new AdvMameScaler(format);
+}
+
 const char *AdvMamePlugin::getName() const {
 	return "advmame";
 }
diff --git a/graphics/scaler/scalebit.h b/graphics/scaler/scalebit.h
index 3ccdf71c45..e85a5aeca2 100644
--- a/graphics/scaler/scalebit.h
+++ b/graphics/scaler/scalebit.h
@@ -41,17 +41,14 @@
 int scale_precondition(unsigned scale, unsigned pixel, unsigned width, unsigned height);
 void scale(unsigned scale, void* void_dst, unsigned dst_slice, const void* void_src, unsigned src_slice, unsigned pixel, unsigned width, unsigned height);
 
-class AdvMamePlugin : public ScalerPluginObject {
+class AdvMameScaler : public Scaler {
 public:
-	AdvMamePlugin();
-	virtual void scaleIntern(const uint8 *srcPtr, uint32 srcPitch,
-							uint8 *dstPtr, uint32 dstPitch, int width, int height, int x, int y) override;
+	AdvMameScaler(const Graphics::PixelFormat &format) : Scaler(format) { _factor = 2; }
 	virtual uint increaseFactor() override;
 	virtual uint decreaseFactor() override;
-	virtual bool canDrawCursor() const override { return true; }
-	virtual uint extraPixels() const override { return 4; }
-	virtual const char *getName() const override;
-	virtual const char *getPrettyName() const override;
+protected:
+	virtual void scaleIntern(const uint8 *srcPtr, uint32 srcPitch,
+							uint8 *dstPtr, uint32 dstPitch, int width, int height, int x, int y) override;
 };
 
 #endif
diff --git a/graphics/scaler/tv.cpp b/graphics/scaler/tv.cpp
index dd37582563..1a3b482e3b 100644
--- a/graphics/scaler/tv.cpp
+++ b/graphics/scaler/tv.cpp
@@ -23,12 +23,7 @@
 #include "graphics/scaler.h"
 #include "graphics/colormasks.h"
 
-TVPlugin::TVPlugin() {
-	_factor = 2;
-	_factors.push_back(2);
-}
-
-void TVPlugin::scaleIntern(const uint8 *srcPtr, uint32 srcPitch,
+void TVScaler::scaleIntern(const uint8 *srcPtr, uint32 srcPitch,
 							uint8 *dstPtr, uint32 dstPitch, int width, int height, int x, int y) {
 	if (_format.bytesPerPixel == 2) {
 		if (_format.gLoss == 2)
@@ -44,24 +39,16 @@ void TVPlugin::scaleIntern(const uint8 *srcPtr, uint32 srcPitch,
 
 }
 
-uint TVPlugin::increaseFactor() {
+uint TVScaler::increaseFactor() {
 	return _factor;
 }
 
-uint TVPlugin::decreaseFactor() {
+uint TVScaler::decreaseFactor() {
 	return _factor;
 }
 
-const char *TVPlugin::getName() const {
-	return "tv";
-}
-
-const char *TVPlugin::getPrettyName() const {
-	return "TV";
-}
-
 template<typename ColorMask>
-void TVPlugin::scaleIntern(const uint8 *srcPtr, uint32 srcPitch, uint8 *dstPtr, uint32 dstPitch,
+void TVScaler::scaleIntern(const uint8 *srcPtr, uint32 srcPitch, uint8 *dstPtr, uint32 dstPitch,
 					int width, int height) {
 	typedef typename ColorMask::PixelType Pixel;
 
@@ -94,4 +81,33 @@ void TVPlugin::scaleIntern(const uint8 *srcPtr, uint32 srcPitch, uint8 *dstPtr,
 	}
 }
 
+
+class TVPlugin final : public ScalerPluginObject {
+public:
+	TVPlugin();
+
+	virtual Scaler *createInstance(const Graphics::PixelFormat &format) const override;
+
+	virtual bool canDrawCursor() const override { return false; }
+	virtual uint extraPixels() const override { return 0; }
+	virtual const char *getName() const override;
+	virtual const char *getPrettyName() const override;
+};
+
+TVPlugin::TVPlugin() {
+	_factors.push_back(2);
+}
+
+Scaler *TVPlugin::createInstance(const Graphics::PixelFormat &format) const {
+	return new TVScaler(format);
+}
+
+const char *TVPlugin::getName() const {
+	return "tv";
+}
+
+const char *TVPlugin::getPrettyName() const {
+	return "TV";
+}
+
 REGISTER_PLUGIN_STATIC(TV, PLUGIN_TYPE_SCALER, TVPlugin);
diff --git a/graphics/scaler/tv.h b/graphics/scaler/tv.h
index c56b97d4f9..82743a4173 100644
--- a/graphics/scaler/tv.h
+++ b/graphics/scaler/tv.h
@@ -24,15 +24,11 @@
 
 #include "graphics/scalerplugin.h"
 
-class TVPlugin : public ScalerPluginObject {
+class TVScaler : public Scaler {
 public:
-	TVPlugin();
+	TVScaler(const Graphics::PixelFormat &format) : Scaler(format) { _factor = 2; }
 	virtual uint increaseFactor() override;
 	virtual uint decreaseFactor() override;
-	virtual bool canDrawCursor() const override { return false; }
-	virtual uint extraPixels() const override { return 0; }
-	virtual const char *getName() const override;
-	virtual const char *getPrettyName() const override;
 private:
 	virtual void scaleIntern(const uint8 *srcPtr, uint32 srcPitch,
 							uint8 *dstPtr, uint32 dstPitch, int width, int height, int x, int y) override;
diff --git a/graphics/scalerplugin.cpp b/graphics/scalerplugin.cpp
index b2904de60d..85b7e013ea 100644
--- a/graphics/scalerplugin.cpp
+++ b/graphics/scalerplugin.cpp
@@ -21,10 +21,6 @@
 
 #include "graphics/scalerplugin.h"
 
-void ScalerPluginObject::initialize(const Graphics::PixelFormat &format) {
-	_format = format;
-}
-
 namespace {
 /**
  * Trivial 'scaler' - in fact it doesn't do any scaling but just copies the
@@ -47,7 +43,7 @@ void Normal1x(const uint8 *srcPtr, uint32 srcPitch, uint8 *dstPtr, uint32 dstPit
 }
 } // End of anonymous namespace
 
-void ScalerPluginObject::scale(const uint8 *srcPtr, uint32 srcPitch, uint8 *dstPtr,
+void Scaler::scale(const uint8 *srcPtr, uint32 srcPitch, uint8 *dstPtr,
 	                           uint32 dstPitch, int width, int height, int x, int y) {
 	if (_factor == 1) {
 		if (_format.bytesPerPixel == 2) {
@@ -60,17 +56,14 @@ void ScalerPluginObject::scale(const uint8 *srcPtr, uint32 srcPitch, uint8 *dstP
 	}
 }
 
-SourceScaler::SourceScaler() : _width(0), _height(0), _oldSrc(NULL), _enable(false) {
+SourceScaler::SourceScaler(const Graphics::PixelFormat &format) : Scaler(format), _width(0), _height(0), _oldSrc(NULL), _enable(false) {
 }
 
 SourceScaler::~SourceScaler() {
 	if (_oldSrc != NULL)
 		delete[] _oldSrc;
-}
 
-void SourceScaler::deinitialize() {
 	_bufferedOutput.free();
-	ScalerPluginObject::deinitialize();
 }
 
 void SourceScaler::setSource(const byte *src, uint pitch, int width, int height, int padding) {
diff --git a/graphics/scalerplugin.h b/graphics/scalerplugin.h
index 2fb10bc785..928e69f7fa 100644
--- a/graphics/scalerplugin.h
+++ b/graphics/scalerplugin.h
@@ -26,23 +26,10 @@
 #include "graphics/pixelformat.h"
 #include "graphics/surface.h"
 
-class ScalerPluginObject : public PluginObject {
+class Scaler {
 public:
-
-	virtual ~ScalerPluginObject() {}
-
-	/**
-	 * This function will be called before any scaler is used.
-	 * Precomputed data should be generated here.
-	 * @param format The pixel format to scale.
-	 */
-	virtual void initialize(const Graphics::PixelFormat &format);
-
-	/**
-	 * This is called when the plugin is not needed. It should clean
-	 * up memory from the initialize method.
-	 */
-	virtual void deinitialize() {}
+	Scaler(const Graphics::PixelFormat &format) : _format(format) {}
+	virtual ~Scaler() {}
 
 	/**
 	 * Scale a rect.
@@ -73,18 +60,6 @@ public:
 
 	virtual uint getFactor() const { return _factor; }
 
-	virtual const Common::Array<uint> &getFactors() const { return _factors; }
-
-	virtual bool hasFactor(uint factor) {
-		const Common::Array<uint> &factors = getFactors();
-		for (Common::Array<uint>::const_iterator it = factors.begin(); it != factors.end(); it++) {
-			if ((*it) == factor)
-				return true;
-		}
-
-		return false;
-	}
-
 	/**
 	 * Set the scaling factor.
 	 * Intended to be used with GUI to set a known valid factor.
@@ -98,34 +73,6 @@ public:
 		return oldFactor;
 	}
 
-	/**
-	 * Indicates how far outside the scaling region this scaler "looks"
-	 * @return The number of pixels in any direction
-	 */
-	virtual uint extraPixels() const = 0;
-
-	/**
-	 * Some scalers are not suitable for scaling the cursor.
-	 * Blurring scalers should return false.
-	 */
-	virtual bool canDrawCursor() const = 0;
-
-	/**
-	 * This value will be displayed on the GUI.
-	 */
-	virtual const char *getPrettyName() const = 0;
-
-	/**
-	 * Computationally intense scalers can benefit from comparing new and old
-	 * source images and updating only the pixels necessary. If the function
-	 * returns true, this scaler prefers this method and the backend can
-	 * optionally use it.
-	 *
-	 * @see enableSource
-	 * @see setSource
-	 */
-	virtual bool useOldSource() const { return false; }
-
 	/**
 	 * Set the source to be used when scaling and copying to the old buffer.
 	 *
@@ -156,7 +103,6 @@ protected:
 	                         uint32 dstPitch, int width, int height, int x, int y) = 0;
 
 	uint _factor;
-	Common::Array<uint> _factors;
 	Graphics::PixelFormat _format;
 };
 
@@ -164,15 +110,13 @@ protected:
  * Convenience class that implements some bookkeeping for keeping track of
  * old source images.
  */
-class SourceScaler : public ScalerPluginObject {
+class SourceScaler : public Scaler {
 
 public:
 
-	SourceScaler();
+	SourceScaler(const Graphics::PixelFormat &format);
 	virtual ~SourceScaler();
 
-	virtual void deinitialize() override;
-
 	virtual void setSource(const byte *src, uint pitch, int width, int height, int padding) final;
 
 	virtual void enableSource(bool enable) final { _enable = enable; }
@@ -204,6 +148,62 @@ private:
 	Graphics::Surface _bufferedOutput;
 };
 
+class ScalerPluginObject : public PluginObject {
+public:
+
+	virtual ~ScalerPluginObject() {}
+
+	virtual Scaler *createInstance(const Graphics::PixelFormat &format) const = 0;
+
+	const Common::Array<uint> &getFactors() const { return _factors; }
+
+	bool hasFactor(uint factor) const {
+		const Common::Array<uint> &factors = getFactors();
+		for (Common::Array<uint>::const_iterator it = factors.begin(); it != factors.end(); it++) {
+			if ((*it) == factor)
+				return true;
+		}
+
+		return false;
+	}
+
+	/**
+	 * Indicates how far outside the scaling region this scaler "looks"
+	 * @return The number of pixels in any direction
+	 */
+	virtual uint extraPixels() const = 0;
+
+	/**
+	 * Some scalers are not suitable for scaling the cursor.
+	 * Blurring scalers should return false.
+	 */
+	virtual bool canDrawCursor() const = 0;
+
+	/**
+	 * This value will be displayed on the GUI.
+	 */
+	virtual const char *getPrettyName() const = 0;
+
+	/**
+	 * The default scale factor.
+	 */
+	virtual uint getDefaultFactor() const { return 2; }
+
+	/**
+	 * Computationally intense scalers can benefit from comparing new and old
+	 * source images and updating only the pixels necessary. If the function
+	 * returns true, this scaler prefers this method and the backend can
+	 * optionally use it.
+	 *
+	 * @see enableSource
+	 * @see setSource
+	 */
+	virtual bool useOldSource() const { return false; }
+
+protected:
+	Common::Array<uint> _factors;
+};
+
 /**
  * Singleton class to manage scaler plugins
  */
diff --git a/gui/options.cpp b/gui/options.cpp
index f817e347f3..304537252f 100644
--- a/gui/options.cpp
+++ b/gui/options.cpp
@@ -646,9 +646,10 @@ void OptionsDialog::apply() {
 				ConfMan.removeKey("scale_factor", _domain);
 
 				uint defaultScaler = g_system->getDefaultScaler();
+				uint defaultScaleFactor = g_system->getDefaultScaleFactor();
 				if (g_system->getScaler() != defaultScaler)
 					graphicsModeChanged = true;
-				else if (scalerPlugins[defaultScaler]->get<ScalerPluginObject>().getFactor() != g_system->getDefaultScaleFactor())
+				else if (g_system->getScaleFactor() != defaultScaleFactor)
 					graphicsModeChanged = true;
 			}
 
@@ -1857,7 +1858,12 @@ void OptionsDialog::updateScaleFactors(uint32 tag) {
 		for (Common::Array<uint>::const_iterator it = factors.begin(); it != factors.end(); it++) {
 			_scaleFactorPopUp->appendEntry(Common::U32String::format("%dx", (*it)), (*it));
 		}
-		_scaleFactorPopUp->setSelectedTag(scalerPlugins[tag]->get<ScalerPluginObject>().getFactor());
+
+		if (g_system->getScaler() == tag) {
+			_scaleFactorPopUp->setSelectedTag(g_system->getScaleFactor());
+		} else {
+			_scaleFactorPopUp->setSelectedTag(scalerPlugins[tag]->get<ScalerPluginObject>().getDefaultFactor());
+		}
 	} else {
 		_scaleFactorPopUp->clearEntries();
 		_scaleFactorPopUp->appendEntry(_("<default>"));


Commit: 00203d7ef97b1a93e78d4502ad3b16b49fd6995c
    https://github.com/scummvm/scummvm/commit/00203d7ef97b1a93e78d4502ad3b16b49fd6995c
Author: Cameron Cawley (ccawley2011 at gmail.com)
Date: 2021-12-09T22:00:32+01:00

Commit Message:
OPENGL: Restore cursor scaling

Changed paths:
    backends/graphics/opengl/opengl-graphics.cpp


diff --git a/backends/graphics/opengl/opengl-graphics.cpp b/backends/graphics/opengl/opengl-graphics.cpp
index 9c691358f2..756d0ab8c4 100644
--- a/backends/graphics/opengl/opengl-graphics.cpp
+++ b/backends/graphics/opengl/opengl-graphics.cpp
@@ -779,6 +779,13 @@ void OpenGLGraphicsManager::setMouseCursor(const void *buf, uint w, uint h, int
 		delete _cursor;
 		_cursor = nullptr;
 
+#ifdef USE_SCALERS
+		bool wantScaler = (_currentState.scaleFactor > 1) && !dontScale
+		                && _scalerPlugins[_currentState.scalerIndex]->get<ScalerPluginObject>().canDrawCursor();
+#else
+		bool wantScaler = false;
+#endif
+
 		GLenum glIntFormat, glFormat, glType;
 
 		Graphics::PixelFormat textureFormat;
@@ -793,10 +800,14 @@ void OpenGLGraphicsManager::setMouseCursor(const void *buf, uint w, uint h, int
 		} else {
 			textureFormat = _defaultFormatAlpha;
 		}
-		// TODO: Enable SW scaling for cursors
-		_cursor = createSurface(textureFormat, true);
+		_cursor = createSurface(textureFormat, true, wantScaler);
 		assert(_cursor);
 		_cursor->enableLinearFiltering(_currentState.filtering);
+#ifdef USE_SCALERS
+		if (wantScaler) {
+			_cursor->setScaler(_currentState.scalerIndex, _currentState.scaleFactor);
+		}
+#endif
 	}
 
 	_cursor->allocate(w, h);


Commit: 95bc1c46edc8408dd8fe444bfcb2878a03f687dc
    https://github.com/scummvm/scummvm/commit/95bc1c46edc8408dd8fe444bfcb2878a03f687dc
Author: Cameron Cawley (ccawley2011 at gmail.com)
Date: 2021-12-09T22:00:32+01:00

Commit Message:
OPENGL: Fix crash when scaling small areas

Changed paths:
    backends/graphics/opengl/texture.cpp


diff --git a/backends/graphics/opengl/texture.cpp b/backends/graphics/opengl/texture.cpp
index f63068deef..67ebadba19 100644
--- a/backends/graphics/opengl/texture.cpp
+++ b/backends/graphics/opengl/texture.cpp
@@ -544,8 +544,13 @@ void ScaledTexture::updateGLTexture() {
 	dst = (byte *)outSurf->getBasePtr(dirtyArea.left * _scaleFactor, dirtyArea.top * _scaleFactor);
 	dstPitch = outSurf->pitch;
 
-	assert(_scaler);
-	_scaler->scale(src, srcPitch, dst, dstPitch, dirtyArea.width(), dirtyArea.height(), dirtyArea.left, dirtyArea.top);
+	if (_scaler && (uint)dirtyArea.height() >= _extraPixels) {
+		_scaler->scale(src, srcPitch, dst, dstPitch, dirtyArea.width(), dirtyArea.height(), dirtyArea.left, dirtyArea.top);
+	} else {
+		Graphics::scaleBlit(dst, src, dstPitch, srcPitch,
+		                    dirtyArea.width() * _scaleFactor, dirtyArea.height() * _scaleFactor,
+		                    dirtyArea.width(), dirtyArea.height(), outSurf->format);
+	}
 
 	dirtyArea.left   *= _scaleFactor;
 	dirtyArea.right  *= _scaleFactor;


Commit: 216bcc3ae3cf50efb6e0577f1d7e96913eacf391
    https://github.com/scummvm/scummvm/commit/216bcc3ae3cf50efb6e0577f1d7e96913eacf391
Author: Cameron Cawley (ccawley2011 at gmail.com)
Date: 2021-12-09T22:00:32+01:00

Commit Message:
NEWS: Mention scaler support in OpenGL mode

Changed paths:
    NEWS.md


diff --git a/NEWS.md b/NEWS.md
index 8740930d8e..1d8e845c6d 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -8,6 +8,7 @@ For a more comprehensive changelog of the latest experimental code, see:
    - Fixed edge case for Punycode.
    - Fixed checking for savegame overwrite in autosave slot.
    - Fixed moving savegame to new slot for most engines.
+   - Scalers are now supported with the OpenGL graphics mode.
 
  AGOS:
    - Fixed old Waxworks AdLib music regression.




More information about the Scummvm-git-logs mailing list