[Scummvm-git-logs] scummvm branch-2-9 -> 583660fb4cb4561f8980448a89763775359be416

mikrosk noreply at scummvm.org
Sun Apr 20 19:41:06 UTC 2025


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

Summary:
f735ad6598 BACKENDS: ATARI: graphics backend fixes from master
583660fb4c BACKENDS: ATARI: Fix crash with certain audio settings


Commit: f735ad6598f1ff751de9d672fe05eda12f4e24b9
    https://github.com/scummvm/scummvm/commit/f735ad6598f1ff751de9d672fe05eda12f4e24b9
Author: Miro Kropacek (miro.kropacek at gmail.com)
Date: 2025-04-20T21:40:32+02:00

Commit Message:
BACKENDS: ATARI: graphics backend fixes from master

Changed paths:
    backends/graphics/atari/atari-cursor.cpp
    backends/graphics/atari/atari-cursor.h
    backends/graphics/atari/atari-graphics-supervidel.h
    backends/graphics/atari/atari-graphics-videl.h
    backends/graphics/atari/atari-graphics.cpp
    backends/graphics/atari/atari-graphics.h
    backends/graphics/atari/atari-screen.cpp
    backends/graphics/atari/atari-screen.h
    backends/platform/atari/osystem_atari.cpp
    backends/platform/atari/osystem_atari.h
    backends/platform/atari/readme.txt
    backends/platform/atari/readme.txt.in
    graphics/blit/blit-atari.cpp


diff --git a/backends/graphics/atari/atari-cursor.cpp b/backends/graphics/atari/atari-cursor.cpp
index b109a45fc75..f621a50c106 100644
--- a/backends/graphics/atari/atari-cursor.cpp
+++ b/backends/graphics/atari/atari-cursor.cpp
@@ -23,18 +23,24 @@
 
 #include <cassert>
 
-#include "graphics/blit.h"
-
 #include "atari-graphics.h"
 #include "atari-screen.h"
-
-extern bool g_unalignedPitch;
+//#include "backends/platform/atari/atari-debug.h"
 
 byte Cursor::_palette[256*3] = {};
 
+Cursor::Cursor(const AtariGraphicsManager *manager, const Screen *screen, int x, int y)
+		: _manager(manager)
+		, _parentScreen(screen)
+		, _boundingSurf(screen->offsettedSurf)
+		, _x(x)
+		, _y(y) {
+}
+
 void Cursor::update() {
 	if (!_buf) {
 		_outOfScreen = true;
+		_savedRect = _alignedDstRect = Common::Rect();
 		return;
 	}
 
@@ -49,13 +55,33 @@ void Cursor::update() {
 		_x - _hotspotX + _width,	// right
 		_y - _hotspotY + _height);	// bottom
 
-	_outOfScreen = !_parentScreen->offsettedSurf->clip(_srcRect, _dstRect);
+	_outOfScreen = !_boundingSurf->clip(_srcRect, _dstRect);
+
+	if (!_outOfScreen) {
+		assert(_srcRect.width() == _dstRect.width());
+		assert(_srcRect.height() == _dstRect.height());
 
-	assert(_srcRect.width() == _dstRect.width());
-	assert(_srcRect.height() == _dstRect.height());
+		const int dstBitsPerPixel = _manager->getBitsPerPixel(_parentScreen->offsettedSurf->format);
+
+		// non-direct rendering never uses 4bpp but maybe in the future ...
+		_savedRect = _manager->alignRect(
+			_dstRect.left * dstBitsPerPixel / 8,	// fake 4bpp by 8bpp's x/2
+			_dstRect.top,
+			_dstRect.right * dstBitsPerPixel / 8,	// fake 4bpp by 8bpp's width/2
+			_dstRect.bottom);
+
+		// this is used only in flushBackground()
+		_alignedDstRect = _manager->alignRect(
+			_dstRect.left + _xOffset,
+			_dstRect.top,
+			_dstRect.right + _xOffset,
+			_dstRect.bottom);
+	}
 }
 
 void Cursor::updatePosition(int deltaX, int deltaY) {
+	//atari_debug("Cursor::updatePosition: %d, %d", deltaX, deltaX);
+
 	if (deltaX == 0 && deltaY == 0)
 		return;
 
@@ -64,13 +90,13 @@ void Cursor::updatePosition(int deltaX, int deltaY) {
 
 	if (_x < 0)
 		_x = 0;
-	else if (_x >= _parentScreen->offsettedSurf->w)
-		_x = _parentScreen->offsettedSurf->w - 1;
+	else if (_x >= _boundingSurf->w)
+		_x = _boundingSurf->w - 1;
 
 	if (_y < 0)
 		_y = 0;
-	else if (_y >= _parentScreen->offsettedSurf->h)
-		_y = _parentScreen->offsettedSurf->h - 1;
+	else if (_y >= _boundingSurf->h)
+		_y = _boundingSurf->h - 1;
 
 	_positionChanged = true;
 }
@@ -97,7 +123,7 @@ void Cursor::setPalette(const byte *colors, uint start, uint num) {
 	_surfaceChanged = true;
 }
 
-void Cursor::convertTo(const Graphics::PixelFormat &format) {
+void Cursor::convertSurfaceTo(const Graphics::PixelFormat &format) {
 	const int cursorWidth = (_srcRect.width() + 15) & (-16);
 	const int cursorHeight = _height;
 	const bool isCLUT8 = format.isCLUT8();
@@ -115,6 +141,7 @@ void Cursor::convertTo(const Graphics::PixelFormat &format) {
 
 		_surface.create(cursorWidth, cursorHeight, format);
 
+		extern bool g_unalignedPitch;
 		const bool old_unalignedPitch = g_unalignedPitch;
 		g_unalignedPitch = true;
 		_surfaceMask.create(_surface.w / 8, _surface.h, format);	// 1 bpl
@@ -169,63 +196,59 @@ void Cursor::convertTo(const Graphics::PixelFormat &format) {
 	}
 }
 
-void Cursor::flushBackground(const Graphics::Surface &srcSurface, const Common::Rect &rect) {
+Common::Rect Cursor::flushBackground(const Common::Rect &alignedRect, bool directRendering) {
 	if (_savedRect.isEmpty())
-		return;
+		return _savedRect;
+
+	if (!alignedRect.isEmpty() && alignedRect.contains(_alignedDstRect)) {
+		// better would be _visibilityChanged but update() ignores it
+		_positionChanged = true;
 
-	if (rect.contains(_savedRect)) {
 		_savedRect = Common::Rect();
-	} else if (rect.intersects(_savedRect)) {
-		restoreBackground(srcSurface, true);
+	} else if (alignedRect.isEmpty() || alignedRect.intersects(_alignedDstRect)) {
+		// better would be _visibilityChanged but update() ignores it
+		_positionChanged = true;
+
+		if (directRendering)
+			restoreBackground();
+		else
+			return _alignedDstRect;
 	}
+
+	return Common::Rect();
 }
 
-bool Cursor::restoreBackground(const Graphics::Surface &srcSurface, bool force) {
-	if (_savedRect.isEmpty() || (!force && !isChanged()))
-		return false;
+void Cursor::saveBackground() {
+	if (_savedRect.isEmpty())
+		return;
 
+	// as this is used only for direct rendering, we don't need to worry about offsettedSurf
+	// having different dimensions than the source surface
 	Graphics::Surface &dstSurface = *_parentScreen->offsettedSurf;
-	const int dstBitsPerPixel     = _manager->getBitsPerPixel(dstSurface.format);
 
-	//atari_debug("Cursor::restoreBackground: %d %d %d %d", _savedRect.left, _savedRect.top, _savedRect.width(), _savedRect.height());
+	//atari_debug("Cursor::saveBackground: %d %d %d %d", _savedRect.left, _savedRect.top, _savedRect.width(), _savedRect.height());
 
-	if (srcSurface.getPixels()) {
-		_manager->copyRectToSurface(
-			dstSurface, dstBitsPerPixel, srcSurface,
-			_savedRect.left, _savedRect.top,
-			_savedRect);
-	} else {
-		const int bytesPerPixel = dstSurface.format.bytesPerPixel;
-
-		// restore native pixels (i.e. bitplanes)
-		Graphics::copyBlit(
-			(byte *)dstSurface.getPixels() + _savedRect.top * dstSurface.pitch + _savedRect.left * dstBitsPerPixel / 8,
-			(const byte *)_savedBackground.getPixels(),
-			dstSurface.pitch, _savedBackground.pitch,
-			_savedRect.width() * dstBitsPerPixel / 8, _savedRect.height(),	// fake 4bpp by 8bpp's width/2
-			bytesPerPixel);
+	// save native pixels (i.e. bitplanes)
+	if (_savedBackground.w != _savedRect.width()
+		|| _savedBackground.h != _savedRect.height()
+		|| _savedBackground.format != dstSurface.format) {
+		_savedBackground.create(_savedRect.width(), _savedRect.height(), dstSurface.format);
 	}
 
-	_savedRect = Common::Rect();
-	return true;
+	_savedBackground.copyRectToSurface(dstSurface, 0, 0, _savedRect);
 }
 
-bool Cursor::draw(bool directRendering, bool force) {
-	if (!isVisible() || (!force && !isChanged()))
-		return false;
-
+void Cursor::draw() {
 	Graphics::Surface &dstSurface = *_parentScreen->offsettedSurf;
 	const int dstBitsPerPixel     = _manager->getBitsPerPixel(dstSurface.format);
 
 	//atari_debug("Cursor::draw: %d %d %d %d", _dstRect.left, _dstRect.top, _dstRect.width(), _dstRect.height());
 
-	// always work with aligned rect
-	_savedRect = _manager->alignRect(_dstRect);
+	if (_surfaceChanged || _srcRect != _previousSrcRect) {
+		_previousSrcRect = _srcRect;
 
-	if (_surfaceChanged || _width != _srcRect.width()) {
-		// TODO: check for change, not just different width so it's not called over and over again ...
-		// TODO: some sort of in-place C2P directly into convertTo() ...
-		convertTo(dstSurface.format);
+		// TODO: some sort of in-place C2P directly into convertSurfaceTo() ...
+		convertSurfaceTo(dstSurface.format);
 		{
 			// c2p in-place (will do nothing on regular Surface::copyRectToSurface)
 			Graphics::Surface surf;
@@ -236,36 +259,40 @@ bool Cursor::draw(bool directRendering, bool force) {
 				_surface.getPixels(),
 				_surface.format);
 			_manager->copyRectToSurface(
-				surf, dstBitsPerPixel, _surface,
+				surf, _surface,
 				0, 0,
 				Common::Rect(_surface.w, _surface.h));
 		}
 	}
 
-	if (directRendering) {
-		// store native pixels (i.e. bitplanes)
-		if (_savedBackground.w != _savedRect.width()
-			|| _savedBackground.h != _savedRect.height()
-			|| _savedBackground.format != dstSurface.format) {
-			_savedBackground.create(_savedRect.width(), _savedRect.height(), dstSurface.format);
-			_savedBackground.pitch = _savedBackground.pitch * dstBitsPerPixel / 8;
-		}
-
-		Graphics::copyBlit(
-			(byte *)_savedBackground.getPixels(),
-			(const byte *)dstSurface.getPixels() + _savedRect.top * dstSurface.pitch + _savedRect.left * dstBitsPerPixel / 8,
-			_savedBackground.pitch, dstSurface.pitch,
-			_savedRect.width() * dstBitsPerPixel / 8, _savedRect.height(),	// fake 4bpp by 8bpp's width/2
-			dstSurface.format.bytesPerPixel);
-	}
-
 	// don't use _srcRect.right as 'x2' as this must be aligned first
-	// (_surface.w is recalculated thanks to convertTo())
+	// (_surface.w is recalculated thanks to convertSurfaceTo())
 	_manager->drawMaskedSprite(
-		dstSurface, dstBitsPerPixel, _surface, _surfaceMask,
-		_dstRect.left, _dstRect.top,
+		dstSurface,
+		_surface, _surfaceMask,
+		_dstRect.left + _xOffset, _dstRect.top,
 		Common::Rect(0, _srcRect.top, _surface.w, _srcRect.bottom));
 
 	_visibilityChanged = _positionChanged = _surfaceChanged = false;
-	return true;
+}
+
+void Cursor::restoreBackground() {
+	if (_savedRect.isEmpty())
+		return;
+
+	assert(_savedBackground.getPixels());
+
+	//atari_debug("Cursor::restoreBackground: %d %d %d %d", _savedRect.left, _savedRect.top, _savedRect.width(), _savedRect.height());
+
+	// as this is used only for direct rendering, we don't need to worry about offsettedSurf
+	// having different dimensions than the source surface
+	Graphics::Surface &dstSurface = *_parentScreen->offsettedSurf;
+
+	// restore native pixels (i.e. bitplanes)
+	dstSurface.copyRectToSurface(
+		_savedBackground,
+		_savedRect.left, _savedRect.top,
+		Common::Rect(_savedBackground.w, _savedBackground.h));
+
+	_savedRect = Common::Rect();
 }
diff --git a/backends/graphics/atari/atari-cursor.h b/backends/graphics/atari/atari-cursor.h
index 2de90f5da3a..7413182e549 100644
--- a/backends/graphics/atari/atari-cursor.h
+++ b/backends/graphics/atari/atari-cursor.h
@@ -25,6 +25,7 @@
 #include "common/rect.h"
 #include "common/scummsys.h"
 #include "graphics/surface.h"
+//#include "backends/platform/atari/atari-debug.h"
 
 class AtariGraphicsManager;
 struct Screen;
@@ -36,17 +37,17 @@ struct Screen;
 // These always get updates by ScummVM, no need to differentiate between engines and the overlay.
 
 struct Cursor {
-	Cursor(AtariGraphicsManager *manager, Screen *screen)
-		: _manager(manager)
-		, _parentScreen(screen) {
-	}
+	Cursor(const AtariGraphicsManager *manager, const Screen *screen, int x, int y);
+
+	void reset(const Graphics::Surface *boundingSurf, int xOffset) {
+		_boundingSurf = boundingSurf;
+		_xOffset = xOffset;
 
-	void reset() {
 		_positionChanged = true;
 		_surfaceChanged = true;
 		_visibilityChanged = false;
 
-		_savedRect = Common::Rect();
+		_savedRect = _previousSrcRect = _alignedDstRect = Common::Rect();
 	}
 
 	// updates outOfScreen OR srcRect/dstRect (only if visible/needed)
@@ -72,6 +73,8 @@ struct Cursor {
 		return Common::Point(_x, _y);
 	}
 	void setPosition(int x, int y) {
+		//atari_debug("Cursor::setPosition: %d, %d", x, y);
+
 		if (_x == x && _y == y)
 			return;
 
@@ -85,7 +88,7 @@ struct Cursor {
 	// surface
 	void setSurface(const void *buf, int w, int h, int hotspotX, int hotspotY, uint32 keycolor);
 	void setPalette(const byte *colors, uint start, uint num);
-	void convertTo(const Graphics::PixelFormat &format);
+	void convertSurfaceTo(const Graphics::PixelFormat &format);
 
 	bool isVisible() const {
 		return !_outOfScreen && _visible;
@@ -94,32 +97,35 @@ struct Cursor {
 		return _positionChanged || _surfaceChanged || _visibilityChanged;
 	}
 
-	bool intersects(const Common::Rect &rect) const {
-		return rect.intersects(_dstRect);
-	}
-
-	void flushBackground(const Graphics::Surface &srcSurface, const Common::Rect &rect);
-	bool restoreBackground(const Graphics::Surface &srcSurface, bool force);
-	bool draw(bool directRendering, bool force);
+	Common::Rect flushBackground(const Common::Rect &alignedRect, bool directRendering);
+	void saveBackground();
+	void draw();
 
 private:
+	void restoreBackground();
+
 	static byte _palette[256*3];
 
-	AtariGraphicsManager *_manager;
-	Screen *_parentScreen;
+	const AtariGraphicsManager *_manager;
+	const Screen *_parentScreen;
+	const Graphics::Surface *_boundingSurf;
+	int _xOffset = 0;
 
 	bool _positionChanged = true;
 	bool _surfaceChanged = true;
 	bool _visibilityChanged = false;
 
 	bool _visible = false;
-	int _x = -1, _y = -1;
+	int _x;
+	int _y;
 	bool _outOfScreen = true;
 	Common::Rect _srcRect;
 	Common::Rect _dstRect;
 
-	Graphics::Surface _savedBackground;	// used by direct rendering
+	Graphics::Surface _savedBackground;
 	Common::Rect _savedRect;
+	Common::Rect _previousSrcRect;
+	Common::Rect _alignedDstRect;
 
 	// related to 'surface'
 	const byte *_buf = nullptr;
@@ -129,6 +135,10 @@ private:
 	int _hotspotY;
 	uint32 _keycolor;
 
+	// TODO: make all surface-related variables and functions static, similar to _palette/
+	// but there's a catch: we still need _surfaceChanged instantiated and convertTo may
+	// be called when clipping changes. Perhaps Cursor should be a singleton and those
+	// flags moved to Screen...
 	Graphics::Surface _surface;
 	Graphics::Surface _surfaceMask;
 	int _rShift, _gShift, _bShift;
diff --git a/backends/graphics/atari/atari-graphics-supervidel.h b/backends/graphics/atari/atari-graphics-supervidel.h
index 8dc7affe37f..28e34303331 100644
--- a/backends/graphics/atari/atari-graphics-supervidel.h
+++ b/backends/graphics/atari/atari-graphics-supervidel.h
@@ -101,11 +101,11 @@ private:
 		return [](void *ptr) { Mfree((uintptr)ptr & 0x00FFFFFF); };
 	}
 
-	void drawMaskedSprite(Graphics::Surface &dstSurface, int dstBitsPerPixel,
+	void drawMaskedSprite(Graphics::Surface &dstSurface,
 						  const Graphics::Surface &srcSurface, const Graphics::Surface &srcMask,
 						  int destX, int destY,
-						  const Common::Rect &subRect) override {
-		assert(dstBitsPerPixel == 8);
+						  const Common::Rect &subRect) const override {
+		assert(dstSurface.format == Graphics::PixelFormat::createFormatCLUT8());
 		assert(subRect.width() % 16 == 0);
 		assert(subRect.width() == srcSurface.w);
 
@@ -122,7 +122,7 @@ private:
 				const uint16 m = *mask;
 
 				if (m == 0xFFFF) {
-					// all 16 pixels transparentm6
+					// all 16 pixels transparent
 					src += 16;
 					dst += 16;
 					continue;
@@ -145,10 +145,6 @@ private:
 		}
 	}
 
-	Common::Rect alignRect(int x, int y, int w, int h) const override {
-		return Common::Rect(x, y, x + w, y + h);
-	}
-
 	static long hasSvRamBoosted() {
 		register long ret __asm__ ("d0") = 0;
 
diff --git a/backends/graphics/atari/atari-graphics-videl.h b/backends/graphics/atari/atari-graphics-videl.h
index 862409b91f1..15126436e67 100644
--- a/backends/graphics/atari/atari-graphics-videl.h
+++ b/backends/graphics/atari/atari-graphics-videl.h
@@ -41,9 +41,16 @@ public:
 	}
 
 private:
-	void copyRectToSurface(Graphics::Surface &dstSurface, int dstBitsPerPixel, const Graphics::Surface &srcSurface,
+	void copyRectToSurface(Graphics::Surface &dstSurface, const Graphics::Surface &srcSurface,
 						   int destX, int destY,
 						   const Common::Rect &subRect) const override {
+		assert(subRect.left % 16 == 0);
+		assert(subRect.width() % 16 == 0);
+		assert(destX % 16 == 0);
+		assert(srcSurface.format == dstSurface.format);
+
+		const int bitsPerPixel = getBitsPerPixel(dstSurface.format);
+
 		// 'pChunkyEnd' is a delicate parameter: the c2p routine compares it to the address register
 		// used for pixel reading; two common mistakes:
 		// 1. (subRect.left, subRect.bottom) = beginning of the next line *including the offset*
@@ -51,9 +58,9 @@ private:
 		const byte *pChunky    = (const byte *)srcSurface.getBasePtr(subRect.left, subRect.top);
 		const byte *pChunkyEnd = (const byte *)srcSurface.getBasePtr(subRect.right, subRect.bottom-1);
 
-        byte *pScreen = (byte *)dstSurface.getPixels() + destY * dstSurface.pitch + destX * dstBitsPerPixel/8;
+		byte *pScreen = (byte *)dstSurface.getPixels() + destY * dstSurface.pitch + destX * bitsPerPixel/8;
 
-		if (dstBitsPerPixel == 8) {
+		if (bitsPerPixel == 8) {
 			if (srcSurface.pitch == subRect.width()) {
 				if (srcSurface.pitch == dstSurface.pitch) {
 					asm_c2p1x1_8(pChunky, pChunkyEnd, pScreen);
@@ -85,17 +92,23 @@ private:
 		}
 	}
 
-	void drawMaskedSprite(Graphics::Surface &dstSurface, int dstBitsPerPixel,
+	void drawMaskedSprite(Graphics::Surface &dstSurface,
 						  const Graphics::Surface &srcSurface, const Graphics::Surface &srcMask,
 						  int destX, int destY,
-						  const Common::Rect &subRect) override {
-		if (dstBitsPerPixel == 4) {
+						  const Common::Rect &subRect) const override {
+		assert(subRect.width() % 16 == 0);
+		assert(subRect.width() == srcSurface.w);
+		assert(srcSurface.format == dstSurface.format);
+
+		const int bitsPerPixel = getBitsPerPixel(dstSurface.format);
+
+		if (bitsPerPixel == 4) {
 			asm_draw_4bpl_sprite(
 				(uint16 *)dstSurface.getPixels(), (const uint16 *)srcSurface.getBasePtr(subRect.left, subRect.top),
 				(const uint16 *)srcMask.getBasePtr(subRect.left, subRect.top),
 				destX, destY,
 				dstSurface.pitch, subRect.width(), subRect.height());
-		} else if (dstBitsPerPixel == 8) {
+		} else if (bitsPerPixel == 8) {
 			asm_draw_8bpl_sprite(
 				(uint16 *)dstSurface.getPixels(), (const uint16 *)srcSurface.getBasePtr(subRect.left, subRect.top),
 				(const uint16 *)srcMask.getBasePtr(subRect.left, subRect.top),
@@ -103,10 +116,6 @@ private:
 				dstSurface.pitch, subRect.width(), subRect.height());
 		}
 	}
-
-	Common::Rect alignRect(int x, int y, int w, int h) const override {
-		return Common::Rect(x & (-16), y, (x + w + 15) & (-16), y + h);
-	}
 };
 
 #endif
diff --git a/backends/graphics/atari/atari-graphics.cpp b/backends/graphics/atari/atari-graphics.cpp
index 84c8c1c59ea..097fb6a35d3 100644
--- a/backends/graphics/atari/atari-graphics.cpp
+++ b/backends/graphics/atari/atari-graphics.cpp
@@ -43,7 +43,6 @@
 
 #define SCREEN_ACTIVE
 
-bool g_unalignedPitch = false;
 mspace g_mspace = nullptr;
 
 static const Graphics::PixelFormat PIXELFORMAT_CLUT8 = Graphics::PixelFormat::createFormatCLUT8();
@@ -408,6 +407,7 @@ OSystem::TransactionError AtariGraphicsManager::endGFXTransaction() {
 	atari_debug("endGFXTransaction");
 
 	_pendingState.inTransaction = false;
+	_ignoreCursorChanges = false;
 
 	int error = OSystem::TransactionError::kTransactionSuccess;
 	bool hasPendingGraphicsMode = false;
@@ -422,10 +422,17 @@ OSystem::TransactionError AtariGraphicsManager::endGFXTransaction() {
 	}
 
 	if (_pendingState.width > 0 && _pendingState.height > 0) {
+		extern bool g_unalignedPitch;
+
 		if (_pendingState.width > getMaximumScreenWidth() || _pendingState.height > getMaximumScreenHeight()) {
 			error |= OSystem::TransactionError::kTransactionSizeChangeFailed;
-		} else if (_pendingState.width % 16 != 0 && !hasSuperVidel()) {
-			atari_warning("Requested width not divisible by 16, please report");
+		} else if (((hasPendingGraphicsMode && _pendingState.mode == kDirectRendering)
+				|| (!hasPendingGraphicsMode && _currentState.mode == kDirectRendering))
+			&& (_pendingState.width % 16 != 0 || g_unalignedPitch)
+			&& !hasSuperVidel()) {
+			atari_warning("Engine surfaces not divisible by 16, aborting");
+			// engineDone is not called
+			g_unalignedPitch = false;
 			error |= OSystem::TransactionError::kTransactionSizeChangeFailed;
 		} else if (_overlayState == kOverlayIgnoredHide || _currentState.width != _pendingState.width || _currentState.height != _pendingState.height) {
 			// if kOverlayIgnoredHide and with valid w/h, force a video mode reset
@@ -453,13 +460,25 @@ OSystem::TransactionError AtariGraphicsManager::endGFXTransaction() {
 	}
 
 	if ((hasPendingGraphicsMode || hasPendingSize) && _currentState.isValid()) {
-		_chunkySurface.init(_currentState.width, _currentState.height, _currentState.width,
+		int c2pWidth = _currentState.width;
+
+		if (!hasSuperVidel()) {
+			// make sure that c2p width is always divisible by 16
+			c2pWidth = (c2pWidth + 15) & -16;
+		}
+
+		_chunkySurface.init(c2pWidth, _currentState.height, c2pWidth,
 			_chunkySurface.getPixels(), _currentState.format);
 
-		_screen[kFrontBuffer]->reset(_currentState.width, _currentState.height, 8, true);
+		const int xOffset = (c2pWidth - _currentState.width) / 2;
+
+		_chunkySurfaceOffsetted.init(_currentState.width, _currentState.height, c2pWidth,
+			_chunkySurface.getBasePtr(xOffset, 0), _currentState.format);
+
+		_screen[kFrontBuffer]->reset(c2pWidth, _currentState.height, 8, _chunkySurfaceOffsetted, xOffset, true);
 		if (_currentState.mode > kSingleBuffering) {
-			_screen[kBackBuffer1]->reset(_currentState.width, _currentState.height, 8, true);
-			_screen[kBackBuffer2]->reset(_currentState.width, _currentState.height, 8, true);
+			_screen[kBackBuffer1]->reset(c2pWidth, _currentState.height, 8, _chunkySurfaceOffsetted, xOffset, true);
+			_screen[kBackBuffer2]->reset(c2pWidth, _currentState.height, 8, _chunkySurfaceOffsetted, xOffset, true);
 		}
 
 		if (hasPendingSize)
@@ -473,7 +492,6 @@ OSystem::TransactionError AtariGraphicsManager::endGFXTransaction() {
 		_pendingScreenChanges.queuePalette();
 
 		if (_overlayState == kOverlayIgnoredHide) {
-			_checkUnalignedPitch = true;
 			_overlayState = kOverlayHidden;
 			_ignoreHideOverlay = false;
 			_pendingScreenChanges.queueAll();
@@ -542,14 +560,18 @@ void AtariGraphicsManager::copyRectToScreen(const void *buf, int pitch, int x, i
 
 	Graphics::Surface &dstSurface = *lockScreen();
 
-	copyRectToScreenInternal(
+	const bool directRendering = _currentState.mode == kDirectRendering;
+
+	addDirtyRectToScreens(
 		dstSurface,
-		buf, pitch, x, y, w, h,
-		_currentState.format, _currentState.mode == kDirectRendering);
+		x, y, w, h,
+		directRendering);
 
-	unlockScreenInternal(
+	copyRectToScreenInternal(
 		dstSurface,
-		x, y, w, h);
+		buf, pitch, x, y, w, h,
+		_currentState.format,
+		directRendering);
 }
 
 Graphics::Surface *AtariGraphicsManager::lockScreen() {
@@ -557,7 +579,7 @@ Graphics::Surface *AtariGraphicsManager::lockScreen() {
 
 	return _currentState.mode == kDirectRendering
 		? _screen[kFrontBuffer]->offsettedSurf
-		: &_chunkySurface;
+		: &_chunkySurfaceOffsetted;
 }
 
 void AtariGraphicsManager::unlockScreen() {
@@ -565,9 +587,10 @@ void AtariGraphicsManager::unlockScreen() {
 
 	//atari_debug("unlockScreen: %d x %d", dstSurface.w, dstSurface.h);
 
-	unlockScreenInternal(
+	addDirtyRectToScreens(
 		dstSurface,
-		0, 0, dstSurface.w, dstSurface.h);
+		0, 0, dstSurface.w, dstSurface.h,
+		_currentState.mode == kDirectRendering);
 }
 
 void AtariGraphicsManager::fillScreen(uint32 col) {
@@ -602,36 +625,6 @@ void AtariGraphicsManager::updateScreen() {
 	// avoid falling into the atari_debugger (screen may not not initialized yet)
 	Common::setErrorHandler(nullptr);
 
-	if (_checkUnalignedPitch) {
-		const Common::ConfigManager::Domain *activeDomain = ConfMan.getActiveDomain();
-		if (activeDomain) {
-			// FIXME: Some engines are too bound to linear surfaces that it is very
-			// hard to repair them. So instead of polluting the engine with
-			// Surface::init() & delete[] Surface::getPixels() just use this hack.
-			const Common::String engineId = activeDomain->getValOrDefault("engineid");
-			const Common::String gameId = activeDomain->getValOrDefault("gameid");
-
-			atari_debug("checking %s/%s", engineId.c_str(), gameId.c_str());
-
-			if (engineId == "composer"
-				|| engineId == "hypno"
-				|| engineId == "mohawk"
-				|| engineId == "parallaction"
-				|| engineId == "private"
-				|| (engineId == "sci"
-					&& (gameId == "phantasmagoria" || gameId == "shivers"))
-				|| engineId == "sherlock"
-				|| engineId == "teenagent"
-				|| engineId == "tsage") {
-				g_unalignedPitch = true;
-			} else {
-				g_unalignedPitch = false;
-			}
-		}
-
-		_checkUnalignedPitch = false;
-	}
-
 	Screen *workScreen = nullptr;
 	Graphics::Surface *srcSurface = nullptr;
 	if (_overlayState == kOverlayVisible || _overlayState == kOverlayIgnoredHide) {
@@ -657,12 +650,9 @@ void AtariGraphicsManager::updateScreen() {
 	}
 
 	assert(workScreen);
-	workScreen->cursor.update();
 
 	bool screenUpdated = updateScreenInternal(workScreen, srcSurface ? *srcSurface : Graphics::Surface());
 
-	workScreen->clearDirtyRects();
-
 #ifdef SCREEN_ACTIVE
 	// this assume that the screen surface is not going to be used yet
 	_pendingScreenChanges.applyBeforeVblLock(*workScreen);
@@ -743,13 +733,17 @@ void AtariGraphicsManager::showOverlay(bool inGUI) {
 	}
 
 	if (_currentState.mode == kDirectRendering) {
-		_screen[kFrontBuffer]->cursor.restoreBackground(Graphics::Surface(), true);
+		_screen[kFrontBuffer]->cursor.flushBackground(Common::Rect(), true);
 	}
 
 	_pendingScreenChanges.setScreenSurface(&_screen[kOverlayBuffer]->surf);
 
-	// do not cache dirtyRects and oldCursorRect
-	_screen[kOverlayBuffer]->reset(getOverlayWidth(), getOverlayHeight(), getBitsPerPixel(getOverlayFormat()), false);
+	// do not cache dirtyRects and saved cursor rect
+	_screen[kOverlayBuffer]->reset(
+		getOverlayWidth(), getOverlayHeight(),
+		getBitsPerPixel(getOverlayFormat()),
+		*lockOverlay(), 0,
+		false);
 
 	_overlayState = kOverlayVisible;
 
@@ -805,7 +799,7 @@ void AtariGraphicsManager::clearOverlay() {
 		return;
 
 	const Graphics::Surface &sourceSurface =
-		_currentState.mode == kDirectRendering ? *_screen[kFrontBuffer]->offsettedSurf : _chunkySurface;
+		_currentState.mode == kDirectRendering ? *_screen[kFrontBuffer]->offsettedSurf : _chunkySurfaceOffsetted;
 
 	const bool upscale = _overlaySurface.w / sourceSurface.w >= 2 && _overlaySurface.h / sourceSurface.h >= 2;
 
@@ -873,7 +867,7 @@ void AtariGraphicsManager::clearOverlay() {
 	// right rect
 	_overlaySurface.fillRect(Common::Rect(_overlaySurface.w - hzOffset, vOffset, _overlaySurface.w, _overlaySurface.h - vOffset), 0);
 
-	_screen[kOverlayBuffer]->addDirtyRect(_overlaySurface, Common::Rect(_overlaySurface.w, _overlaySurface.h), false);
+	_screen[kOverlayBuffer]->addDirtyRect(_overlaySurface, 0, 0, _overlaySurface.w, _overlaySurface.h, false);
 }
 
 void AtariGraphicsManager::grabOverlay(Graphics::Surface &surface) const {
@@ -896,41 +890,53 @@ void AtariGraphicsManager::grabOverlay(Graphics::Surface &surface) const {
 void AtariGraphicsManager::copyRectToOverlay(const void *buf, int pitch, int x, int y, int w, int h) {
 	//atari_debug("copyRectToOverlay: %d, %d, %d(%d), %d", x, y, w, pitch, h);
 
+	Graphics::Surface &dstSurface = *lockOverlay();
+
 	const bool directRendering = isOverlayDirectRendering();
 
-	Graphics::Surface &dstSurface = directRendering
-		? *_screen[kOverlayBuffer]->offsettedSurf
-		: _overlaySurface;
+	_screen[kOverlayBuffer]->addDirtyRect(
+		dstSurface,
+		x, y, w, h,
+		directRendering);
 
 	copyRectToScreenInternal(
 		dstSurface,
 		buf, pitch, x, y, w, h,
 		getOverlayFormat(),
 		directRendering);
+}
 
-	const Common::Rect rect = alignRect(x, y, w, h);
-	_screen[kOverlayBuffer]->addDirtyRect(dstSurface, rect, directRendering);
+Graphics::Surface *AtariGraphicsManager::lockOverlay() {
+	//atari_debug("lockOverlay");
+
+	return isOverlayDirectRendering()
+	   ? _screen[kOverlayBuffer]->offsettedSurf
+	   : &_overlaySurface;
 }
 
 bool AtariGraphicsManager::showMouse(bool visible) {
-	//atari_debug("showMouse: %d", visible);
+	//atari_debug("showMouse: %d; ignored: %d", visible, _ignoreCursorChanges);
 
-	bool last;
+	if (_ignoreCursorChanges)
+		return visible;
 
-	if (isOverlayVisible()) {
-		last = _screen[kOverlayBuffer]->cursor.setVisible(visible);
-	} else if (_currentState.mode <= kSingleBuffering) {
-		last = _screen[kFrontBuffer]->cursor.setVisible(visible);
-	} else {
-		last = _screen[kBackBuffer1]->cursor.setVisible(visible);
+	bool lastOverlay, lastFront, lastBack1 = false;
+
+	// TODO: cursor.flushBackground() if !visible
+	lastOverlay = _screen[kOverlayBuffer]->cursor.setVisible(visible);
+	lastFront   = _screen[kFrontBuffer]->cursor.setVisible(visible);
+
+	if (_currentState.mode == kTripleBuffering) {
+		lastBack1 = _screen[kBackBuffer1]->cursor.setVisible(visible);
 		_screen[kBackBuffer2]->cursor.setVisible(visible);
-		_screen[kFrontBuffer]->cursor.setVisible(visible);
 	}
 
-	// don't rely on engines to call it (if they don't it confuses the cursor restore logic)
-	updateScreen();
-
-	return last;
+	if (isOverlayVisible())
+		return lastOverlay;
+	else if (_currentState.mode <= kSingleBuffering)
+		return lastFront;
+	else
+		return lastBack1;
 }
 
 void AtariGraphicsManager::warpMouse(int x, int y) {
@@ -949,7 +955,11 @@ void AtariGraphicsManager::warpMouse(int x, int y) {
 
 void AtariGraphicsManager::setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor,
 										  bool dontScale, const Graphics::PixelFormat *format, const byte *mask) {
-	//atari_debug("setMouseCursor: %d, %d, %d, %d, %d, %d", w, h, hotspotX, hotspotY, keycolor, format ? format->bytesPerPixel : 1);
+	//atari_debug("setMouseCursor: %d, %d, %d, %d, %d, %d; ignored: %d",
+	//	w, h, hotspotX, hotspotY, keycolor, format ? format->bytesPerPixel : 1, _ignoreCursorChanges);
+
+	if (_ignoreCursorChanges)
+		return;
 
 	if (mask)
 		atari_warning("AtariGraphicsManager::setMouseCursor: Masks are not supported");
@@ -957,27 +967,25 @@ void AtariGraphicsManager::setMouseCursor(const void *buf, uint w, uint h, int h
 	if (format)
 		assert(*format == PIXELFORMAT_CLUT8);
 
-	if (isOverlayVisible()) {
-		_screen[kOverlayBuffer]->cursor.setSurface(buf, (int)w, (int)h, hotspotX, hotspotY, keycolor);
-	} else if (_currentState.mode <= kSingleBuffering) {
-		_screen[kFrontBuffer]->cursor.setSurface(buf, (int)w, (int)h, hotspotX, hotspotY, keycolor);
-	} else {
+	_screen[kOverlayBuffer]->cursor.setSurface(buf, (int)w, (int)h, hotspotX, hotspotY, keycolor);
+	_screen[kFrontBuffer]->cursor.setSurface(buf, (int)w, (int)h, hotspotX, hotspotY, keycolor);
+
+	if (_currentState.mode == kTripleBuffering) {
 		_screen[kBackBuffer1]->cursor.setSurface(buf, (int)w, (int)h, hotspotX, hotspotY, keycolor);
 		_screen[kBackBuffer2]->cursor.setSurface(buf, (int)w, (int)h, hotspotX, hotspotY, keycolor);
-		_screen[kFrontBuffer]->cursor.setSurface(buf, (int)w, (int)h, hotspotX, hotspotY, keycolor);
 	}
 }
 
 void AtariGraphicsManager::setCursorPalette(const byte *colors, uint start, uint num) {
 	atari_debug("setCursorPalette: %d, %d", start, num);
 
-	if (isOverlayVisible()) {
-		// cursor palette is supported only in the overlay
-		_screen[kOverlayBuffer]->cursor.setPalette(colors, start, num);
-	}
+	// cursor palette is supported only in the overlay
+	_screen[kOverlayBuffer]->cursor.setPalette(colors, start, num);
 }
 
 void AtariGraphicsManager::updateMousePosition(int deltaX, int deltaY) {
+	//atari_debug("updateMousePosition: %d, %d", deltaX, deltaY);
+
 	if (isOverlayVisible()) {
 		_screen[kOverlayBuffer]->cursor.updatePosition(deltaX, deltaY);
 	} else if (_currentState.mode <= kSingleBuffering) {
@@ -997,10 +1005,12 @@ bool AtariGraphicsManager::notifyEvent(const Common::Event &event) {
 			// clear work screen: this is needed if *next* game shows an error upon startup
 			Graphics::Surface &surf = _currentState.mode == kDirectRendering
 				? *_screen[kFrontBuffer]->offsettedSurf
-				: _chunkySurface;
+				: _chunkySurfaceOffsetted;
 			surf.fillRect(Common::Rect(surf.w, surf.h), 0);
 
 			_ignoreHideOverlay = true;
+			// gui manager would want to hide overlay, set game cursor etc
+			_ignoreCursorChanges = true;
 			return false;
 		}
 		break;
@@ -1043,6 +1053,10 @@ Common::Keymap *AtariGraphicsManager::getKeymap() const {
 	return keymap;
 }
 
+int AtariGraphicsManager::getBitsPerPixel(const Graphics::PixelFormat &format) const {
+	return format == PIXELFORMAT_RGB121 ? 4 : 8;
+}
+
 void AtariGraphicsManager::allocateSurfaces() {
 	for (int i : { kFrontBuffer, kBackBuffer1, kBackBuffer2 }) {
 		_screen[i] = new Screen(this, getMaximumScreenWidth(), getMaximumScreenHeight(), PIXELFORMAT_CLUT8, &_palette);
@@ -1050,6 +1064,7 @@ void AtariGraphicsManager::allocateSurfaces() {
 	_screen[kOverlayBuffer] = new Screen(this, getOverlayWidth(), getOverlayHeight(), getOverlayFormat(), &_overlayPalette);
 
 	_chunkySurface.create(getMaximumScreenWidth(), getMaximumScreenHeight(), PIXELFORMAT_CLUT8);
+	_chunkySurfaceOffsetted = _chunkySurface;
 	_overlaySurface.create(getOverlayWidth(), getOverlayHeight(), getOverlayFormat());
 }
 
@@ -1060,17 +1075,16 @@ void AtariGraphicsManager::freeSurfaces() {
 	}
 
 	_chunkySurface.free();
+	_chunkySurfaceOffsetted = _chunkySurface;
 	_overlaySurface.free();
 }
 
-void AtariGraphicsManager::unlockScreenInternal(const Graphics::Surface &dstSurface, int x, int y, int w, int h) {
-	const bool directRendering = _currentState.mode == kDirectRendering;
-	const Common::Rect rect = alignRect(x, y, w, h);
-	_screen[kFrontBuffer]->addDirtyRect(dstSurface, rect, directRendering);
+void AtariGraphicsManager::addDirtyRectToScreens(const Graphics::Surface &dstSurface, int x, int y, int w, int h, bool directRendering) {
+	_screen[kFrontBuffer]->addDirtyRect(dstSurface, x, y, w, h, directRendering);
 
 	if (_currentState.mode > kSingleBuffering) {
-		_screen[kBackBuffer1]->addDirtyRect(dstSurface, rect, directRendering);
-		_screen[kBackBuffer2]->addDirtyRect(dstSurface, rect, directRendering);
+		_screen[kBackBuffer1]->addDirtyRect(dstSurface, x, y, w, h, directRendering);
+		_screen[kBackBuffer2]->addDirtyRect(dstSurface, x, y, w, h, directRendering);
 	}
 }
 
@@ -1082,30 +1096,42 @@ bool AtariGraphicsManager::updateScreenInternal(Screen *dstScreen, const Graphic
 	Cursor &cursor                       = dstScreen->cursor;
 
 	const bool directRendering           = srcSurface.getPixels() == nullptr;
-	const int dstBitsPerPixel            = getBitsPerPixel(dstSurface->format);
 
 	bool updated = false;
 
-	const bool cursorDrawEnabled = cursor.isVisible();
-	bool forceCursorDraw = cursorDrawEnabled && (dstScreen->fullRedraw || cursor.isChanged());
-
 	lockSuperBlitter();
 
-	for (auto it = dirtyRects.begin(); it != dirtyRects.end(); ++it) {
-		if (cursorDrawEnabled && !forceCursorDraw)
-			forceCursorDraw = cursor.intersects(*it);
-
-		if (!directRendering) {
-			copyRectToSurface(*dstSurface, dstBitsPerPixel, srcSurface, it->left, it->top, *it);
+	if (cursor.isChanged()) {
+		const Common::Rect cursorBackgroundRect = cursor.flushBackground(Common::Rect(), directRendering);
+		if (!cursorBackgroundRect.isEmpty()) {
+			copyRectToSurface(*dstSurface, srcSurface, cursorBackgroundRect.left, cursorBackgroundRect.top, cursorBackgroundRect);
 			updated |= true;
 		}
 	}
 
-	updated |= cursor.restoreBackground(srcSurface, false);
+	// update cursor rects and visibility flag (if out of screen)
+	cursor.update();
 
+	const bool drawCursor = cursor.isVisible() && (dstScreen->fullRedraw || cursor.isChanged());
+
+	if (!directRendering) {
+		for (auto it = dirtyRects.begin(); it != dirtyRects.end(); ++it) {
+			copyRectToSurface(*dstSurface, srcSurface, it->left, it->top, *it);
+		}
+		updated |= !dirtyRects.empty();
+	} else if (drawCursor) {
+		cursor.saveBackground();
+	}
+
+	// unlock here because cursor.draw() is a software blit
 	unlockSuperBlitter();
 
-	updated |= cursor.draw(directRendering, forceCursorDraw);
+	if (drawCursor) {
+		cursor.draw();
+		updated |= true;
+	}
+
+	dstScreen->clearDirtyRects();
 
 	return updated;
 }
@@ -1113,9 +1139,9 @@ bool AtariGraphicsManager::updateScreenInternal(Screen *dstScreen, const Graphic
 void AtariGraphicsManager::copyRectToScreenInternal(Graphics::Surface &dstSurface,
 													const void *buf, int pitch, int x, int y, int w, int h,
 													const Graphics::PixelFormat &format, bool directRendering) {
-	const Common::Rect rect = alignRect(x, y, w, h);
-
 	if (directRendering) {
+		const Common::Rect rect = alignRect(x, y, x + w, y + h);
+
 		// TODO: mask the unaligned parts and copy the rest
 		Graphics::Surface srcSurface;
 		byte *srcBuf = (byte *)const_cast<void *>(buf);
@@ -1123,24 +1149,10 @@ void AtariGraphicsManager::copyRectToScreenInternal(Graphics::Surface &dstSurfac
 		srcSurface.init(rect.width(), rect.height(), pitch, srcBuf, format);
 
 		copyRectToSurface(
-			dstSurface, getBitsPerPixel(format), srcSurface,
+			dstSurface, srcSurface,
 			rect.left, rect.top,
 			Common::Rect(rect.width(), rect.height()));
 	} else {
 		dstSurface.copyRectToSurface(buf, pitch, x, y, w, h);
 	}
 }
-
-int AtariGraphicsManager::getBitsPerPixel(const Graphics::PixelFormat &format) const {
-	return format == PIXELFORMAT_RGB121 ? 4 : 8;
-}
-
-bool AtariGraphicsManager::isOverlayDirectRendering() const {
-	// overlay is direct rendered if in the launcher or if game is directly rendered
-	// (on SuperVidel we always want to use shading/transparency but its direct rendering is fine and supported)
-	return !hasSuperVidel()
-#ifndef DISABLE_FANCY_THEMES
-		   && (ConfMan.getActiveDomain() == nullptr || _currentState.mode == kDirectRendering)
-#endif
-		;
-}
diff --git a/backends/graphics/atari/atari-graphics.h b/backends/graphics/atari/atari-graphics.h
index fd437228d3d..3672c5554f6 100644
--- a/backends/graphics/atari/atari-graphics.h
+++ b/backends/graphics/atari/atari-graphics.h
@@ -31,6 +31,7 @@
 #include "graphics/surface.h"
 
 #include "atari-cursor.h"
+#include "atari-graphics-superblitter.h"
 #include "atari-pendingscreenchanges.h"
 #include "atari-screen.h"
 
@@ -117,6 +118,7 @@ protected:
 	typedef void* (*AtariMemAlloc)(size_t bytes);
 	typedef void (*AtariMemFree)(void *ptr);
 
+	int getBitsPerPixel(const Graphics::PixelFormat &format) const;
 	void allocateSurfaces();
 	void freeSurfaces();
 
@@ -140,16 +142,40 @@ private:
 	int16 getMaximumScreenWidth() const { return _tt ? 320 : (_vgaMonitor ? 320 : 320*1.2); }
 #endif
 
-	void unlockScreenInternal(const Graphics::Surface &dstSurface,
-							  int x, int y, int w, int h);
+	void addDirtyRectToScreens(const Graphics::Surface &dstSurface,
+							   int x, int y, int w, int h, bool directRendering);
 	bool updateScreenInternal(Screen *dstScreen, const Graphics::Surface &srcSurface);
 	void copyRectToScreenInternal(Graphics::Surface &dstSurface,
 								  const void *buf, int pitch, int x, int y, int w, int h,
 								  const Graphics::PixelFormat &format, bool directRendering);
 
-	int getBitsPerPixel(const Graphics::PixelFormat &format) const;
+	bool isOverlayDirectRendering() const {
+		// see osystem_atari.cpp
+		extern bool g_gameEngineActive;
+
+		// overlay is direct rendered if in the launcher or if game is directly rendered
+		// (on SuperVidel we always want to use shading/transparency but its direct rendering is fine and supported)
+		return !hasSuperVidel()
+#ifndef DISABLE_FANCY_THEMES
+			&& (!g_gameEngineActive || _currentState.mode == kDirectRendering)
+#endif
+			;
+	}
+
+	Graphics::Surface *lockOverlay();
 
-	bool isOverlayDirectRendering() const;
+	Common::Rect alignRect(int x1, int y1, int x2, int y2) const {
+		// make non-virtual for performance reasons
+		return hasSuperVidel()
+				   ? Common::Rect(x1, y1, x2, y2)
+				   : Common::Rect(x1 & (-16), y1, (x2 + 15) & (-16), y2);
+	}
+	Common::Rect alignRect(const Common::Rect &rect) const {
+		// make non-virtual for performance reasons
+		return hasSuperVidel()
+				   ? rect
+				   : Common::Rect(rect.left & (-16), rect.top, (rect.right + 15) & (-16), rect.bottom);
+	}
 
 	virtual AtariMemAlloc getStRamAllocFunc() const {
 		return [](size_t bytes) { return (void*)Mxalloc(bytes, MX_STRAM); };
@@ -158,26 +184,19 @@ private:
 		return [](void *ptr) { Mfree(ptr); };
 	}
 
-	virtual void copyRectToSurface(Graphics::Surface &dstSurface, int dstBitsPerPixel, const Graphics::Surface &srcSurface,
+	virtual void copyRectToSurface(Graphics::Surface &dstSurface, const Graphics::Surface &srcSurface,
 								   int destX, int destY,
 								   const Common::Rect &subRect) const {
 		dstSurface.copyRectToSurface(srcSurface, destX, destY, subRect);
 	}
 
-	virtual void drawMaskedSprite(Graphics::Surface &dstSurface, int dstBitsPerPixel,
+	virtual void drawMaskedSprite(Graphics::Surface &dstSurface,
 								  const Graphics::Surface &srcSurface, const Graphics::Surface &srcMask,
 								  int destX, int destY,
-								  const Common::Rect &subRect) = 0;
-
-	virtual Common::Rect alignRect(int x, int y, int w, int h) const = 0;
-
-	Common::Rect alignRect(const Common::Rect &rect) const {
-		return alignRect(rect.left, rect.top, rect.width(), rect.height());
-	}
+								  const Common::Rect &subRect) const = 0;
 
 	bool _vgaMonitor = true;
 	bool _tt = false;
-	bool _checkUnalignedPitch = false;
 
 	struct GraphicsState {
 		GraphicsState()
@@ -216,6 +235,7 @@ private:
 	Screen *_screen[kBufferCount] = {};
 
 	Graphics::Surface _chunkySurface;
+	Graphics::Surface _chunkySurfaceOffsetted;
 
 	enum {
 		kOverlayVisible,
@@ -225,6 +245,7 @@ private:
 	int _overlayState = kOverlayHidden;
 	bool _ignoreHideOverlay = true;
 	Graphics::Surface _overlaySurface;
+	bool _ignoreCursorChanges = false;
 
 	Palette _palette;
 	Palette _overlayPalette;
diff --git a/backends/graphics/atari/atari-screen.cpp b/backends/graphics/atari/atari-screen.cpp
index 6c6521cd167..dd591a2965d 100644
--- a/backends/graphics/atari/atari-screen.cpp
+++ b/backends/graphics/atari/atari-screen.cpp
@@ -25,10 +25,11 @@
 
 #include "atari-graphics.h"
 #include "atari-graphics-superblitter.h"
+#include "backends/platform/atari/atari-debug.h"
 
 Screen::Screen(AtariGraphicsManager *manager, int width, int height, const Graphics::PixelFormat &format, const Palette *palette_)
 	: _manager(manager)
-	, cursor(manager, this)
+	, cursor(manager, this, width / 2, height / 2)
 	, palette(palette_) {
 	const AtariGraphicsManager::AtariMemAlloc &allocFunc = _manager->getStRamAllocFunc();
 
@@ -44,6 +45,7 @@ Screen::Screen(AtariGraphicsManager *manager, int width, int height, const Graph
 		error("Failed to allocate memory in ST RAM");
 	}
 
+	// TODO: use mspace_calloc similar as what we do with SuperVidel
 	surf.setPixels((void *)(((uintptr)pixelsUnaligned + sizeof(uintptr) + ALIGN - 1) & (-ALIGN)));
 
 	// store the unaligned pointer for later release
@@ -63,11 +65,13 @@ Screen::~Screen() {
 	freeFunc((void *)*((uintptr *)surf.getPixels() - 1));
 }
 
-void Screen::reset(int width, int height, int bitsPerPixel, bool resetCursorPosition) {
+void Screen::reset(int width, int height, int bitsPerPixel, const Graphics::Surface &boundingSurf, int xOffset, bool resetCursorPosition) {
+	_xOffset = xOffset;
+
 	clearDirtyRects();
-	cursor.reset();
+	cursor.reset(&boundingSurf, xOffset);
 	if (resetCursorPosition)
-		cursor.setPosition(width / 2, height / 2);
+		cursor.setPosition(boundingSurf.w / 2, boundingSurf.h / 2);
 	rez = -1;
 	mode = -1;
 
@@ -134,24 +138,34 @@ void Screen::reset(int width, int height, int bitsPerPixel, bool resetCursorPosi
 		surf.format);
 }
 
-void Screen::addDirtyRect(const Graphics::Surface &srcSurface, const Common::Rect &rect, bool directRendering) {
+void Screen::addDirtyRect(const Graphics::Surface &srcSurface, int x, int y, int w, int h, bool directRendering) {
 	if (fullRedraw)
 		return;
 
-	if ((rect.width() == srcSurface.w && rect.height() == srcSurface.h)
+	if ((w == srcSurface.w && h == srcSurface.h)
 		|| dirtyRects.size() == 128) {	// 320x200 can hold at most 250 16x16 rectangles
 		//atari_debug("addDirtyRect[%d]: purge %d x %d", (int)dirtyRects.size(), srcSurface.w, srcSurface.h);
 
 		dirtyRects.clear();
-		dirtyRects.emplace(srcSurface.w, srcSurface.h);
+		// don't use x/y/w/h, the 2nd expression may be true
+		// also, it's ok if e.g. w = 630 gets aligned to w = 640, nothing is drawn in 630~639
+		dirtyRects.insert(_manager->alignRect(_xOffset, 0, _xOffset + srcSurface.w, srcSurface.h));
 
-		cursor.reset();
+		cursor.reset(&srcSurface, _xOffset);
 
 		fullRedraw = true;
 	} else {
-		dirtyRects.insert(rect);
+		const Common::Rect alignedRect = _manager->alignRect(x + _xOffset, y, x + _xOffset + w, y + h);
+
+		dirtyRects.insert(alignedRect);
 
-		// do it now to avoid checking in AtariGraphicsManager::updateScreenInternal()
-		cursor.flushBackground(directRendering ? Graphics::Surface() : srcSurface, rect);
+		// Check whether the cursor background intersects the dirty rect. Has to be done here,
+		// before the actual drawing (especially in case of direct rendering). There's one more
+		// check in AtariGraphicsManager::updateScreenInternal for the case when there are no
+		// dirty rectangles but the cursor itself has changed.
+		const Common::Rect cursorBackgroundRect = cursor.flushBackground(alignedRect, directRendering);
+		if (!cursorBackgroundRect.isEmpty()) {
+			dirtyRects.insert(cursorBackgroundRect);
+		}
 	}
 }
diff --git a/backends/graphics/atari/atari-screen.h b/backends/graphics/atari/atari-screen.h
index abf1b156f7c..2cd49e0d8aa 100644
--- a/backends/graphics/atari/atari-screen.h
+++ b/backends/graphics/atari/atari-screen.h
@@ -63,9 +63,9 @@ struct Screen {
 	Screen(AtariGraphicsManager *manager, int width, int height, const Graphics::PixelFormat &format, const Palette *palette);
 	~Screen();
 
-	void reset(int width, int height, int bitsPerPixel, bool resetCursorPosition);
+	void reset(int width, int height, int bitsPerPixel, const Graphics::Surface &boundingSurf, int xOffset, bool resetCursorPosition);
 	// must be called before any rectangle drawing
-	void addDirtyRect(const Graphics::Surface &srcSurface, const Common::Rect &rect, bool directRendering);
+	void addDirtyRect(const Graphics::Surface &srcSurface, int x, int y, int w, int h, bool directRendering);
 
 	void clearDirtyRects() {
 		dirtyRects.clear();
@@ -98,6 +98,7 @@ private:
 	const AtariGraphicsManager *_manager;
 
 	Graphics::Surface _offsettedSurf;
+	int _xOffset = 0;
 };
 
 #endif // BACKENDS_GRAPHICS_ATARI_SCREEN_H
diff --git a/backends/platform/atari/osystem_atari.cpp b/backends/platform/atari/osystem_atari.cpp
index 597001fab2d..62e9705d6a7 100644
--- a/backends/platform/atari/osystem_atari.cpp
+++ b/backends/platform/atari/osystem_atari.cpp
@@ -66,6 +66,9 @@
  */
 #include "backends/fs/posix/posix-fs-factory.h"
 
+bool g_unalignedPitch = false;
+bool g_gameEngineActive = false;
+
 extern "C" void atari_kbdvec(void *);
 extern "C" void atari_mousevec(void *);
 typedef void (*KBDVEC)(void *);
@@ -347,6 +350,45 @@ void OSystem_Atari::initBackend() {
 	BaseBackend::initBackend();
 }
 
+void OSystem_Atari::engineInit() {
+	//atari_debug("engineInit");
+
+	g_gameEngineActive = true;
+
+	const Common::ConfigManager::Domain *activeDomain = ConfMan.getActiveDomain();
+	assert(activeDomain);
+
+	// FIXME: Some engines are too bound to linear surfaces that it is very
+	// hard to repair them. So instead of polluting the engine with
+	// Surface::init() & delete[] Surface::getPixels() just use this hack.
+	const Common::String engineId = activeDomain->getValOrDefault("engineid");
+	const Common::String gameId = activeDomain->getValOrDefault("gameid");
+
+	atari_debug("checking %s/%s", engineId.c_str(), gameId.c_str());
+
+	if (engineId == "composer"
+		|| engineId == "hypno"
+		|| engineId == "mohawk"
+		|| engineId == "parallaction"
+		|| engineId == "private"
+		|| (engineId == "sci"
+			&& (gameId == "phantasmagoria" || gameId == "shivers"))
+		|| engineId == "sherlock"
+		|| engineId == "teenagent"
+		|| engineId == "tsage") {
+		g_unalignedPitch = true;
+	} else {
+		g_unalignedPitch = false;
+	}
+}
+
+void OSystem_Atari::engineDone() {
+	//atari_debug("engineDone");
+
+	g_gameEngineActive = false;
+	g_unalignedPitch = false;
+}
+
 Common::MutexInternal *OSystem_Atari::createMutex() {
 	return new NullMutexInternal();
 }
@@ -468,6 +510,8 @@ void OSystem_Atari::update() {
 		inTimer = false;
 	} else {
 		const Common::ConfigManager::Domain *activeDomain = ConfMan.getActiveDomain();
+		assert(activeDomain);
+
 		warning("%s/%s calls update() from timer",
 			activeDomain->getValOrDefault("engineid").c_str(),
 			activeDomain->getValOrDefault("gameid").c_str());
diff --git a/backends/platform/atari/osystem_atari.h b/backends/platform/atari/osystem_atari.h
index 6f428a64859..c3be4d8e1a6 100644
--- a/backends/platform/atari/osystem_atari.h
+++ b/backends/platform/atari/osystem_atari.h
@@ -31,6 +31,9 @@ public:
 
 	void initBackend() override;
 
+	void engineInit() override;
+	void engineDone() override;
+
 	Common::MutexInternal *createMutex() override;
 	uint32 getMillis(bool skipRecord = false) override;
 	void delayMillis(uint msecs) override;
diff --git a/backends/platform/atari/readme.txt b/backends/platform/atari/readme.txt
index 5fea469f3c2..63adeeefc33 100644
--- a/backends/platform/atari/readme.txt
+++ b/backends/platform/atari/readme.txt
@@ -479,6 +479,10 @@ Known issues
   - point the extra path to the folder with *.wav files (or copy its content
     where monkey.00? files are located)
 
+- Phantasmagoria, KQ7 (and other SCI32 games) require Single buffering (or
+  Direct rendering with SuperVidel) due to its unusual cursor rendering
+  approach.
+
 - following engines have been explicitly disabled:
   - Cine (2 games)
     - incompatible with other engines / prone to freezes
diff --git a/backends/platform/atari/readme.txt.in b/backends/platform/atari/readme.txt.in
index 30083a4a8e1..c3bd62c9270 100644
--- a/backends/platform/atari/readme.txt.in
+++ b/backends/platform/atari/readme.txt.in
@@ -479,6 +479,10 @@ Known issues
   - point the extra path to the folder with *.wav files (or copy its content
     where monkey.00? files are located)
 
+- Phantasmagoria, KQ7 (and other SCI32 games) require Single buffering (or
+  Direct rendering with SuperVidel) due to its unusual cursor rendering
+  approach.
+
 - following engines have been explicitly disabled:
   - Cine (2 games)
     - incompatible with other engines / prone to freezes
diff --git a/graphics/blit/blit-atari.cpp b/graphics/blit/blit-atari.cpp
index 40753637820..02ba8a6fe88 100644
--- a/graphics/blit/blit-atari.cpp
+++ b/graphics/blit/blit-atari.cpp
@@ -84,8 +84,9 @@ void unlockSuperBlitter() {
 #endif
 }
 
-// see atari-graphics.cpp
+// see osystem-atari.cpp
 extern bool g_unalignedPitch;
+// see atari-graphics.cpp
 extern mspace g_mspace;
 
 namespace Graphics {


Commit: 583660fb4cb4561f8980448a89763775359be416
    https://github.com/scummvm/scummvm/commit/583660fb4cb4561f8980448a89763775359be416
Author: Miro Kropacek (miro.kropacek at gmail.com)
Date: 2025-04-20T21:40:32+02:00

Commit Message:
BACKENDS: ATARI: Fix crash with certain audio settings

E.g. 8195 Hz/2048 samples in KQ7 would lead to crash.

Changed paths:
    backends/mixer/atari/atari-mixer.cpp


diff --git a/backends/mixer/atari/atari-mixer.cpp b/backends/mixer/atari/atari-mixer.cpp
index d7bc2c63f61..82baf1c2308 100644
--- a/backends/mixer/atari/atari-mixer.cpp
+++ b/backends/mixer/atari/atari-mixer.cpp
@@ -107,8 +107,8 @@ void AtariMixerManager::init() {
 	}
 
 	// don't use the recommended number of samples
-	obtained.samples = desired.samples;
 	obtained.size = obtained.size * desired.samples / obtained.samples;
+	obtained.samples = desired.samples;
 
 	_outputRate = obtained.frequency;
 	_outputChannels = obtained.channels;




More information about the Scummvm-git-logs mailing list