[Scummvm-git-logs] scummvm master -> d4a9b8612595323d997348b8ee341f708e062b85

neuromancer noreply at scummvm.org
Thu Apr 30 08:16:21 UTC 2026


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

Summary:
60cff2859c COLONY: improved movement and implement face foward key to reduce confusion when looking up/down
2719eff73f COLONY: fixed automap colors in mac b&w release
08281f3ebd COLONY: improve intro audio timing
9055a677e0 COLONY: avoid choppy mouse cursor during animations
a3f835dd6e COLONY: refactor color packing
d4a9b86125 COLONY: use managed surfaces to increase performance


Commit: 60cff2859c01951b3bf57950b0cf86655d2c4cfa
    https://github.com/scummvm/scummvm/commit/60cff2859c01951b3bf57950b0cf86655d2c4cfa
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-30T10:16:05+02:00

Commit Message:
COLONY: improved movement and implement face foward key to reduce confusion when looking up/down

Changed paths:
    engines/colony/colony.cpp
    engines/colony/colony.h
    engines/colony/metaengine.cpp


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index bb63dd13df0..7f4cbf5c027 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -173,6 +173,9 @@ ColonyEngine::ColonyEngine(OSystem *syst, const ADGameDescription *gd) : Engine(
 	_rotateLeft = false;
 	_rotateRight = false;
 	_sprint = false;
+	_moveAccumX = 0.0f;
+	_moveAccumY = 0.0f;
+	_rotAccum = 0.0f;
 	_wm = nullptr;
 	_macMenu = nullptr;
 	_menuSurface = nullptr;
@@ -928,6 +931,9 @@ Common::Error ColonyEngine::run() {
 				case kActionLookBehind:
 					_me.look = _me.ang + 128;
 					break;
+				case kActionFaceForward:
+					_me.lookY = 0;
+					break;
 				case kActionToggleDashboard:
 					_showDashBoard = !_showDashBoard;
 					break;
@@ -1045,57 +1051,72 @@ Common::Error ColonyEngine::run() {
 			mouseDX = mouseDY = 0;
 		}
 
-		// Apply continuous movement/rotation from held keys,
-		// throttled to ~15 ticks/sec to match original key-repeat feel
-		if (now - lastMoveTick >= 66) {
+		// Smooth, deltaTime-based movement (Freescape-style). Top speed
+		// matches the previous 15Hz tick: units/sec = 120 * (1 << spd).
+		// Sprint (shift) bumps the speed level by 1; speed keys 1-5 select
+		// the base level. Diagonals are normalized so combined input is
+		// not faster than single-axis movement.
+		{
+			float dt = (now - lastMoveTick) / 1000.0f;
 			lastMoveTick = now;
-			const int spd = _sprint ? _speedShift + 1 : _speedShift;
-			const int moveX = (_cost[_me.look] * (1 << spd)) >> 4;
-			const int moveY = (_sint[_me.look] * (1 << spd)) >> 4;
-			const int rotSpeed = 1 << (_speedShift - 1);
-
-			if (_gameMode == kModeBattle) {
-				if (_moveForward)
-					battleCommand(_me.xloc + moveX, _me.yloc + moveY);
-				if (_moveBackward)
-					battleCommand(_me.xloc - moveX, _me.yloc - moveY);
-				if (_strafeLeft) {
-					uint8 strafeAngle = (uint8)((int)_me.look + 64);
-					int sx = (_cost[strafeAngle] * (1 << spd)) >> 4;
-					int sy = (_sint[strafeAngle] * (1 << spd)) >> 4;
-					battleCommand(_me.xloc + sx, _me.yloc + sy);
-				}
-				if (_strafeRight) {
-					uint8 strafeAngle = (uint8)((int)_me.look - 64);
-					int sx = (_cost[strafeAngle] * (1 << spd)) >> 4;
-					int sy = (_sint[strafeAngle] * (1 << spd)) >> 4;
-					battleCommand(_me.xloc + sx, _me.yloc + sy);
+			if (dt > 0.1f)
+				dt = 0.1f; // clamp for first frame / pause-resume
+
+			const int spd = CLIP(_sprint ? _speedShift + 1 : _speedShift, 1, 6);
+			const float speed = 120.0f * (float)(1 << spd); // world units/sec
+
+			float dirX = 0.0f, dirY = 0.0f;
+			if (_moveForward) {
+				dirX += _cost[_me.look];
+				dirY += _sint[_me.look];
+			}
+			if (_moveBackward) {
+				dirX -= _cost[_me.look];
+				dirY -= _sint[_me.look];
+			}
+			if (_strafeLeft) {
+				uint8 a = (uint8)((int)_me.look + 64);
+				dirX += _cost[a];
+				dirY += _sint[a];
+			}
+			if (_strafeRight) {
+				uint8 a = (uint8)((int)_me.look - 64);
+				dirX += _cost[a];
+				dirY += _sint[a];
+			}
+
+			if (dirX != 0.0f || dirY != 0.0f) {
+				const float len = sqrtf(dirX * dirX + dirY * dirY);
+				const float ux = dirX / len;
+				const float uy = dirY / len;
+				_moveAccumX += ux * speed * dt;
+				_moveAccumY += uy * speed * dt;
+				const int ix = (int)_moveAccumX;
+				const int iy = (int)_moveAccumY;
+				_moveAccumX -= ix;
+				_moveAccumY -= iy;
+				if (ix != 0 || iy != 0) {
+					if (_gameMode == kModeBattle)
+						battleCommand(_me.xloc + ix, _me.yloc + iy);
+					else
+						cCommand(_me.xloc + ix, _me.yloc + iy, true);
 				}
 			} else {
-				if (_moveForward)
-					cCommand(_me.xloc + moveX, _me.yloc + moveY, true);
-				if (_moveBackward)
-					cCommand(_me.xloc - moveX, _me.yloc - moveY, true);
-				if (_strafeLeft) {
-					uint8 strafeAngle = (uint8)((int)_me.look + 64);
-					int sx = (_cost[strafeAngle] * (1 << spd)) >> 4;
-					int sy = (_sint[strafeAngle] * (1 << spd)) >> 4;
-					cCommand(_me.xloc + sx, _me.yloc + sy, true);
-				}
-				if (_strafeRight) {
-					uint8 strafeAngle = (uint8)((int)_me.look - 64);
-					int sx = (_cost[strafeAngle] * (1 << spd)) >> 4;
-					int sy = (_sint[strafeAngle] * (1 << spd)) >> 4;
-					cCommand(_me.xloc + sx, _me.yloc + sy, true);
-				}
-			}
-			if (_rotateLeft) {
-				_me.ang += rotSpeed;
-				_me.look += rotSpeed;
+				_moveAccumX = 0.0f;
+				_moveAccumY = 0.0f;
 			}
-			if (_rotateRight) {
-				_me.ang -= rotSpeed;
-				_me.look -= rotSpeed;
+
+			if (_rotateLeft || _rotateRight) {
+				const float rotSpeed = (float)(1 << (_speedShift - 1)) * 15.0f;
+				_rotAccum += (_rotateLeft ? rotSpeed : -rotSpeed) * dt;
+				const int rint = (int)_rotAccum;
+				_rotAccum -= rint;
+				if (rint != 0) {
+					_me.ang = (uint8)((int)_me.ang + rint);
+					_me.look = (uint8)((int)_me.look + rint);
+				}
+			} else {
+				_rotAccum = 0.0f;
 			}
 		}
 
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 5ba166a3a7c..61a7341e329 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -66,6 +66,7 @@ enum ColonyAction {
 	kActionLookLeft,
 	kActionLookRight,
 	kActionLookBehind,
+	kActionFaceForward,
 	kActionToggleMouselook,
 	kActionToggleDashboard,
 	kActionToggleWireframe,
@@ -498,6 +499,13 @@ private:
 	bool _rotateRight;
 	bool _sprint;
 
+	// Sub-unit accumulators for deltaTime-based smooth movement.
+	// Position uses 256-units-per-cell integers, angles are uint8 — these
+	// retain fractional progress between frames so low speeds aren't lost.
+	float _moveAccumX;
+	float _moveAccumY;
+	float _rotAccum;
+
 	Common::RandomSource _randomSource;
 	Common::Point _mousePos;
 	uint8 _decode1[4];
diff --git a/engines/colony/metaengine.cpp b/engines/colony/metaengine.cpp
index 4b88aca2400..a9af9b4517e 100644
--- a/engines/colony/metaengine.cpp
+++ b/engines/colony/metaengine.cpp
@@ -149,6 +149,11 @@ Common::KeymapArray ColonyMetaEngine::initKeymaps(const char *target) const {
 	act->addDefaultInputMapping("x");
 	engineKeyMap->addAction(act);
 
+	act = new Common::Action("FACEFRWARD", _("Face forward"));
+	act->setCustomEngineActionEvent(kActionFaceForward);
+	act->addDefaultInputMapping("f");
+	engineKeyMap->addAction(act);
+
 	act = new Common::Action("MOUSE", _("Toggle mouselook"));
 	act->setCustomEngineActionEvent(kActionToggleMouselook);
 	act->addDefaultInputMapping("SPACE");
@@ -184,7 +189,7 @@ Common::KeymapArray ColonyMetaEngine::initKeymaps(const char *target) const {
 
 	act = new Common::Action("FIRE", _("Fire weapon"));
 	act->setCustomEngineActionEvent(kActionFire);
-	act->addDefaultInputMapping("f");
+	act->addDefaultInputMapping("LCTRL");
 	act->addDefaultInputMapping("JOY_B");
 	engineKeyMap->addAction(act);
 


Commit: 2719eff73fbae65ed76e5c2bc931ef839c8c03c1
    https://github.com/scummvm/scummvm/commit/2719eff73fbae65ed76e5c2bc931ef839c8c03c1
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-30T10:16:05+02:00

Commit Message:
COLONY: fixed automap colors in mac b&w release

Changed paths:
    engines/colony/ui.cpp


diff --git a/engines/colony/ui.cpp b/engines/colony/ui.cpp
index a40a589018a..e0141e5cc75 100644
--- a/engines/colony/ui.cpp
+++ b/engines/colony/ui.cpp
@@ -943,6 +943,7 @@ void ColonyEngine::drawAutomap() {
 
 	const int lv = _level - 1;
 	const bool isMac = (_renderMode == Common::kRenderMacintosh);
+	const bool macColor = isMac && _hasMacColors;
 
 	const Common::Rect vp(0, _menuBarHeight, _width, _height);
 	const int vpW = vp.width();
@@ -950,7 +951,9 @@ void ColonyEngine::drawAutomap() {
 	if (vpW <= 0 || vpH <= 0)
 		return;
 
-	_gfx->fillRect(vp, isMac ? 0xFFA0D0FF : 15);
+	// Match the minimap: B&W Mac is white background + black lines, like the
+	// compass area; color Mac keeps its tinted background.
+	_gfx->fillRect(vp, macColor ? 0xFFA0D0FF : (isMac ? packRGB(255, 255, 255) : 15));
 	_gfx->drawRect(vp, 0);
 
 	const int lExt = MIN(vpW, vpH) / 12;


Commit: 08281f3ebd0f1c04518b0eba1a3e2eff690992c9
    https://github.com/scummvm/scummvm/commit/08281f3ebd0f1c04518b0eba1a3e2eff690992c9
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-30T10:16:05+02:00

Commit Message:
COLONY: improve intro audio timing

Changed paths:
    engines/colony/colony.cpp
    engines/colony/colony.h
    engines/colony/intro.cpp
    engines/colony/metaengine.cpp
    engines/colony/sound.cpp
    engines/colony/sound.h


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index 7f4cbf5c027..4083f778227 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -1164,7 +1164,7 @@ bool ColonyEngine::checkSkipRequested() {
 		case Common::EVENT_RETURN_TO_LAUNCHER:
 			return true;
 		case Common::EVENT_CUSTOM_ENGINE_ACTION_START:
-			if (event.customType == kActionSkipIntro)
+			if (event.customType == kActionEscape)
 				return true;
 			break;
 		case Common::EVENT_SCREEN_CHANGED:
@@ -1196,7 +1196,7 @@ bool ColonyEngine::waitForInput() {
 			case Common::EVENT_RETURN_TO_LAUNCHER:
 				return false;
 			case Common::EVENT_CUSTOM_ENGINE_ACTION_START:
-				if (event.customType == kActionSkipIntro)
+				if (event.customType == kActionEscape)
 					return true;
 				return false;
 			case Common::EVENT_KEYDOWN:
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 61a7341e329..6c59dcd02e4 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -71,7 +71,6 @@ enum ColonyAction {
 	kActionToggleDashboard,
 	kActionToggleWireframe,
 	kActionToggleFullscreen,
-	kActionSkipIntro,
 	kActionEscape,
 	kActionFire
 };
diff --git a/engines/colony/intro.cpp b/engines/colony/intro.cpp
index be70a7aff33..020b284e740 100644
--- a/engines/colony/intro.cpp
+++ b/engines/colony/intro.cpp
@@ -218,7 +218,10 @@ void ColonyEngine::playIntro() {
 			_gfx->clear(_gfx->black());
 			if (!drawPict(-32565))  // Color Colony
 				drawPict(-32748);   // B&W Colony
-			_sound->play(Sound::kMars);
+			// Original intro.c: PlayMars() → PlayCSound(mars), looped via VBL
+			// task. Mars stays alive across the next several intro sections
+			// until EndCSound() is called.
+			_sound->play(Sound::kMars, true);
 			qt = makeStars(_screenR, 0);
 
 			if (!qt) {
@@ -1044,8 +1047,9 @@ bool ColonyEngine::timeSquare(const Common::String &str, const Graphics::Font *m
 	_sound->stop();
 
 	// Phase 3: Mac resumes Mars here; DOS scrolls out silently.
+	// Original intro.c TimeSquare line 323: PlayMars() restarts the loop.
 	if (macStyle)
-		_sound->play(Sound::kMars);
+		_sound->play(Sound::kMars, true);
 	for (int x = targetX; x > endX; x -= stepX) {
 		_gfx->fillRect(textBand, 0);
 		_gfx->drawString(font, str, x, centery + 2, textIndex, Graphics::kTextAlignLeft);
diff --git a/engines/colony/metaengine.cpp b/engines/colony/metaengine.cpp
index a9af9b4517e..717450467c6 100644
--- a/engines/colony/metaengine.cpp
+++ b/engines/colony/metaengine.cpp
@@ -175,13 +175,7 @@ Common::KeymapArray ColonyMetaEngine::initKeymaps(const char *target) const {
 	act->addDefaultInputMapping("F11");
 	engineKeyMap->addAction(act);
 
-	act = new Common::Action("SKIP", _("Skip intro"));
-	act->setCustomEngineActionEvent(kActionSkipIntro);
-	act->addDefaultInputMapping("S+s");
-	act->addDefaultInputMapping("JOY_X");
-	engineKeyMap->addAction(act);
-
-	act = new Common::Action("ESCAPE", _("Menu"));
+	act = new Common::Action("ESCAPE", _("Menu / Skip intro"));
 	act->setCustomEngineActionEvent(kActionEscape);
 	act->addDefaultInputMapping("ESCAPE");
 	act->addDefaultInputMapping("JOY_BACK");
diff --git a/engines/colony/sound.cpp b/engines/colony/sound.cpp
index 3dea5fcc281..9ab0459219d 100644
--- a/engines/colony/sound.cpp
+++ b/engines/colony/sound.cpp
@@ -141,14 +141,14 @@ bool Sound::isPlaying() const {
 	return _vm->_mixer->isSoundHandleActive(_handle) || _speaker->isPlaying();
 }
 
-void Sound::play(int soundID) {
+void Sound::play(int soundID, bool loop) {
 	stop();
 
 	if (!_vm->isSoundEnabled())
 		return;
 
 	if (_vm->getPlatform() == Common::kPlatformMacintosh)
-		playMacSound(soundID);
+		playMacSound(soundID, loop);
 	else
 		playPCSpeaker(soundID);
 }
@@ -345,7 +345,7 @@ void Sound::playPCSpeaker(int soundID) {
 	}
 }
 
-bool Sound::playMacSound(int soundID) {
+bool Sound::playMacSound(int soundID, bool loop) {
 	// Primary resource IDs from original sound.c
 	int resID = -1;
 	switch (soundID) {
@@ -379,7 +379,7 @@ bool Sound::playMacSound(int soundID) {
 	default: break;
 	}
 
-	if (resID != -1 && playResource(resID))
+	if (resID != -1 && playResource(resID, loop))
 		return true;
 
 	// Fallback resource IDs for sounds missing from this binary version.
@@ -392,7 +392,7 @@ bool Sound::playMacSound(int soundID) {
 	default: break;
 	}
 
-	if (altResID != -1 && playResource(altResID))
+	if (altResID != -1 && playResource(altResID, loop))
 		return true;
 
 	// Fallback to DOS sounds if Mac resource is missing
@@ -400,7 +400,7 @@ bool Sound::playMacSound(int soundID) {
 	return false;
 }
 
-bool Sound::playResource(int resID) {
+bool Sound::playResource(int resID, bool loop) {
 	Common::SeekableReadStream *snd = nullptr;
 
 	// Search Zounds first (has most sounds)
@@ -426,7 +426,8 @@ bool Sound::playResource(int resID) {
 	snd->read(data, dataSize);
 	delete snd;
 
-	Audio::AudioStream *stream = Audio::makeRawStream(data, dataSize, 11127, Audio::FLAG_UNSIGNED, DisposeAfterUse::YES);
+	Audio::RewindableAudioStream *raw = Audio::makeRawStream(data, dataSize, 11127, Audio::FLAG_UNSIGNED, DisposeAfterUse::YES);
+	Audio::AudioStream *stream = loop ? Audio::makeLoopingAudioStream(raw, 0) : raw;
 	_vm->_mixer->playStream(Audio::Mixer::kSFXSoundType, &_handle, stream);
 	return true;
 }
diff --git a/engines/colony/sound.h b/engines/colony/sound.h
index 93ddcb601ea..78c8fb936ef 100644
--- a/engines/colony/sound.h
+++ b/engines/colony/sound.h
@@ -43,7 +43,7 @@ public:
 	~Sound();
 
 	void init();
-	void play(int soundID);
+	void play(int soundID, bool loop = false);
 	void stop();
 	bool isPlaying() const;
 
@@ -90,8 +90,8 @@ private:
 	Audio::SoundHandle _handle;
 
 	void playPCSpeaker(int soundID);
-	bool playMacSound(int soundID);
-	bool playResource(int resID);
+	bool playMacSound(int soundID, bool loop);
+	bool playResource(int resID, bool loop);
 };
 
 } // End of namespace Colony


Commit: 9055a677e08827083f37b8f04a8e9692e8e80d40
    https://github.com/scummvm/scummvm/commit/9055a677e08827083f37b8f04a8e9692e8e80d40
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-30T10:16:05+02:00

Commit Message:
COLONY: avoid choppy mouse cursor during animations

Changed paths:
    engines/colony/animation.cpp
    engines/colony/colony.cpp
    engines/colony/colony.h


diff --git a/engines/colony/animation.cpp b/engines/colony/animation.cpp
index 694def0a7b1..16dea3e934b 100644
--- a/engines/colony/animation.cpp
+++ b/engines/colony/animation.cpp
@@ -32,6 +32,7 @@
 #include "common/stream.h"
 #include "common/system.h"
 #include "graphics/cursorman.h"
+#include "graphics/surface.h"
 
 #include "colony/colony.h"
 #include "colony/renderer.h"
@@ -573,6 +574,12 @@ void ColonyEngine::playAnimation() {
 		_gfx->copyToScreen();
 	}
 
+	// drawAnimation is heavy (per-pixel setPixel calls for the 416x264 pattern)
+	// so cap it to the animation update cadence (50ms = 20fps). Between draws,
+	// only call updateScreen so the OpenGL backend re-composites with the
+	// current cursor position — keeps the pointer smooth without re-rendering.
+	uint32 lastDrawTime = 0;
+	bool needsDraw = true;
 	while (_animationRunning && !shouldQuit()) {
 		Common::Event event;
 		while (_system->getEventManager()->pollEvent(event)) {
@@ -581,10 +588,12 @@ void ColonyEngine::playAnimation() {
 				return;
 			} else if (event.type == Common::EVENT_SCREEN_CHANGED) {
 				_gfx->computeScreenViewport();
+				needsDraw = true;
 			} else if (event.type == Common::EVENT_LBUTTONDOWN) {
 				int item = whichSprite(event.mouse);
 				if (item > 0) {
 					handleAnimationClick(item);
+					needsDraw = true;
 				}
 			} else if (event.type == Common::EVENT_RBUTTONDOWN) {
 				// DOS: right-click exits animation (AnimControl returns FALSE on button-up)
@@ -596,6 +605,7 @@ void ColonyEngine::playAnimation() {
 				if (event.customType == kActionEscape) {
 					openMainMenuDialog();
 					_gfx->computeScreenViewport();
+					needsDraw = true;
 				}
 			} else if (event.type == Common::EVENT_KEYDOWN) {
 				int item = 0;
@@ -611,14 +621,28 @@ void ColonyEngine::playAnimation() {
 
 				if (item > 0) {
 					handleAnimationClick(item);
+					needsDraw = true;
 				}
 			}
 		}
 
+		// updateAnimation has its own 50ms throttle; only redraw when we know
+		// the visible state changed (click feedback) or the cadence is due.
+		const uint32 prevAnimUpdate = _lastAnimUpdate;
 		updateAnimation();
-		drawAnimation();
-		_gfx->copyToScreen();
-		responsiveAnimationDelay(_system, 4);
+		if (_lastAnimUpdate != prevAnimUpdate)
+			needsDraw = true;
+
+		const uint32 now = _system->getMillis();
+		if (needsDraw || now - lastDrawTime >= 50) {
+			drawAnimation();
+			_gfx->copyToScreen();
+			lastDrawTime = now;
+			needsDraw = false;
+		} else {
+			_system->updateScreen();
+		}
+		_system->delayMillis(2);
 	}
 
 	_system->lockMouse(true);
@@ -698,44 +722,76 @@ void ColonyEngine::drawAnimation() {
 	//   Top: BMColor[0]<0 -> system color; ==0 -> powered:c_char0+level-1.f, else:c_dwall.b
 	//   Bottom: powered -> c_lwall.f; unpowered -> inherits top BackColor
 	// B&W/DOS: preserve existing palette-index behavior (bit 1 -> 15, bit 0 -> 0).
+	//
+	// We render the pattern into a cached RGBA surface and blit it via
+	// drawSurface (one texture upload per change). The previous implementation
+	// did 416*264 = 109,824 individual setPixel calls per drawAnimation, each
+	// issuing its own glBegin/glEnd; on the OpenGL backend that took tens of
+	// milliseconds per frame and starved the cursor of refreshes.
+	const int patternMode = useColor ? 2 : (_renderMode == Common::kRenderMacintosh ? 1 : 0);
+	uint32 topColor = 0, botColor = 0;
 	if (useColor) {
 		const bool powered = (_corePower[_coreIndex] > 0);
-		uint32 topBG = resolveAnimColor(_animBMColors[0]);
-		// Bottom: only uses c_lwall.f when powered; unpowered inherits top color
-		uint32 botBG = powered ? packMacColorBG(_macColors[kMcLwall].fg) : topBG;
-		for (int y = 0; y < 264; y++) {
-			byte *pat = (y < _divideBG) ? _topBG : _bottomBG;
-			byte row = pat[y % 8];
-			uint32 bg = (y < _divideBG) ? topBG : botBG;
-			for (int x = 0; x < 416; x++) {
-				bool set = (row & (0x80 >> (x % 8))) != 0;
-				_gfx->setPixel(ox + x, oy + y, set ? (uint32)0xFF000000 : bg);
-			}
-		}
-	} else if (_renderMode == Common::kRenderMacintosh) {
-		// Mac QuickDraw FillRect: pattern bit=1 → ForeColor (black=0),
-		// bit=0 → BackColor (white=15).
-		for (int y = 0; y < 264; y++) {
-			byte *pat = (y < _divideBG) ? _topBG : _bottomBG;
-			byte row = pat[y % 8];
-			for (int x = 0; x < 416; x++) {
-				bool set = (row & (0x80 >> (x % 8))) != 0;
-				_gfx->setPixel(ox + x, oy + y, set ? 0 : 15);
-			}
+		topColor = resolveAnimColor(_animBMColors[0]);
+		botColor = powered ? packMacColorBG(_macColors[kMcLwall].fg) : topColor;
+	}
+
+	const bool keyChanged = !_animPatternValid
+		|| _animPatternKeyMode != patternMode
+		|| _animPatternKeyDivide != _divideBG
+		|| _animPatternKeyTopColor != topColor
+		|| _animPatternKeyBotColor != botColor
+		|| memcmp(_animPatternKeyTopBG, _topBG, 8) != 0
+		|| memcmp(_animPatternKeyBottomBG, _bottomBG, 8) != 0;
+
+	if (!_animPatternSurface) {
+		_animPatternSurface = new Graphics::Surface();
+		_animPatternSurface->create(416, 264, Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0));
+	}
+
+	if (keyChanged) {
+		const Graphics::PixelFormat &fmt = _animPatternSurface->format;
+		uint32 fgPixel, topBgPixel, botBgPixel;
+		switch (patternMode) {
+		case 2: // Mac color: bit=1 → black, bit=0 → topColor/botColor
+			fgPixel = fmt.ARGBToColor(255, 0, 0, 0);
+			topBgPixel = fmt.ARGBToColor(255,
+				(topColor >> 16) & 0xFF, (topColor >> 8) & 0xFF, topColor & 0xFF);
+			botBgPixel = fmt.ARGBToColor(255,
+				(botColor >> 16) & 0xFF, (botColor >> 8) & 0xFF, botColor & 0xFF);
+			break;
+		case 1: // Mac B&W: bit=1 → black, bit=0 → white
+			fgPixel = fmt.ARGBToColor(255, 0, 0, 0);
+			topBgPixel = botBgPixel = fmt.ARGBToColor(255, 255, 255, 255);
+			break;
+		default: // DOS: bit=1 → white, bit=0 → black
+			fgPixel = fmt.ARGBToColor(255, 255, 255, 255);
+			topBgPixel = botBgPixel = fmt.ARGBToColor(255, 0, 0, 0);
+			break;
 		}
-	} else {
-		// DOS MetaWINDOW: pattern bit=1 → pen color (white=15),
-		// bit=0 → background (black=0). Opposite of Mac QuickDraw.
+
+		uint32 *pixels = (uint32 *)_animPatternSurface->getPixels();
 		for (int y = 0; y < 264; y++) {
-			byte *pat = (y < _divideBG) ? _topBG : _bottomBG;
-			byte row = pat[y % 8];
+			const byte row = ((y < _divideBG) ? _topBG : _bottomBG)[y % 8];
+			const uint32 bgPixel = (y < _divideBG) ? topBgPixel : botBgPixel;
+			uint32 *dst = pixels + y * 416;
 			for (int x = 0; x < 416; x++) {
-				bool set = (row & (0x80 >> (x % 8))) != 0;
-				_gfx->setPixel(ox + x, oy + y, set ? 15 : 0);
+				const bool set = (row & (0x80 >> (x % 8))) != 0;
+				dst[x] = set ? fgPixel : bgPixel;
 			}
 		}
+
+		memcpy(_animPatternKeyTopBG, _topBG, 8);
+		memcpy(_animPatternKeyBottomBG, _bottomBG, 8);
+		_animPatternKeyDivide = _divideBG;
+		_animPatternKeyTopColor = topColor;
+		_animPatternKeyBotColor = botColor;
+		_animPatternKeyMode = patternMode;
+		_animPatternValid = true;
 	}
 
+	_gfx->drawSurface(_animPatternSurface, ox, oy);
+
 	// Draw background image if active.
 	// Original: BMColor[1] only applied when corepower[coreindex] > 0.
 	if (_backgroundActive && _backgroundFG) {
diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index 4083f778227..0996a626682 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -279,6 +279,10 @@ ColonyEngine::~ColonyEngine() {
 		_pictCompass->free();
 		delete _pictCompass;
 	}
+	if (_animPatternSurface) {
+		_animPatternSurface->free();
+		delete _animPatternSurface;
+	}
 	delete _frameLimiter;
 	delete _gfx;
 	delete _sound;
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 6c59dcd02e4..3482d847f85 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -714,6 +714,19 @@ private:
 	byte _topBG[8] = {};
 	byte _bottomBG[8] = {};
 	int16 _divideBG;
+
+	// Cache for the animation background pattern. Regenerated only when
+	// pattern bytes / colors / split point change. Replaces a 110k-pixel
+	// setPixel loop with one texture upload, fixing cursor choppiness
+	// during animations on the OpenGL renderer.
+	Graphics::Surface *_animPatternSurface = nullptr;
+	byte _animPatternKeyTopBG[8] = {};
+	byte _animPatternKeyBottomBG[8] = {};
+	int16 _animPatternKeyDivide = -1;
+	uint32 _animPatternKeyTopColor = 0;
+	uint32 _animPatternKeyBotColor = 0;
+	int _animPatternKeyMode = -1; // 0=DOS, 1=Mac B&W, 2=Mac color
+	bool _animPatternValid = false;
 	Common::String _animationName;
 	Common::Array<int16> _animBMColors;
 	bool _animationRunning;


Commit: a3f835dd6e8969c68f15d4e1b5e7d645baf90ba8
    https://github.com/scummvm/scummvm/commit/a3f835dd6e8969c68f15d4e1b5e7d645baf90ba8
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-30T10:16:05+02:00

Commit Message:
COLONY: refactor color packing

Changed paths:
    engines/colony/animation.cpp
    engines/colony/colony.h
    engines/colony/intro.cpp
    engines/colony/movement.cpp
    engines/colony/render_internal.h
    engines/colony/ui.cpp


diff --git a/engines/colony/animation.cpp b/engines/colony/animation.cpp
index 16dea3e934b..05436e4cbe0 100644
--- a/engines/colony/animation.cpp
+++ b/engines/colony/animation.cpp
@@ -243,11 +243,6 @@ uint32 macSysColorToARGB(int sysColor) {
 	}
 }
 
-uint32 packMacColorBG(const uint16 rgb[3]) {
-	return 0xFF000000 | ((uint32)(rgb[0] >> 8) << 16) |
-		((uint32)(rgb[1] >> 8) << 8) | (uint32)(rgb[2] >> 8);
-}
-
 int getAnimationStateCount(const Common::Array<ComplexSprite *> &sprites, int num) {
 	num--;
 	if (num >= 0 && num < (int)sprites.size()) {
@@ -692,15 +687,15 @@ uint32 ColonyEngine::resolveAnimColor(int16 bmEntry) const {
 		return macSysColorToARGB(-bmEntry);
 	} else if (bmEntry > 0) {
 		if (bmEntry < 145)
-			return packMacColorBG(_macColors[bmEntry].bg);
+			return packMacColor(_macColors[bmEntry].bg);
 		return 0xFFFFFFFF;
 	} else {
 		// Zero = level-based (original gamesprt.c DrawlSprite/DrawBackGround):
 		//   if(corepower[coreindex]) RGBBackColor(&cColor[c_char0+level-1].f);
 		//   else RGBBackColor(&cColor[c_dwall].b);
 		if (_corePower[_coreIndex] > 0 && _level >= 1 && _level <= 7)
-			return packMacColorBG(_macColors[kMcChar0 + _level - 1].fg);
-		return packMacColorBG(_macColors[kMcDwall].bg);
+			return packMacColor(_macColors[kMcChar0 + _level - 1].fg);
+		return packMacColor(_macColors[kMcDwall].bg);
 	}
 }
 
@@ -733,7 +728,7 @@ void ColonyEngine::drawAnimation() {
 	if (useColor) {
 		const bool powered = (_corePower[_coreIndex] > 0);
 		topColor = resolveAnimColor(_animBMColors[0]);
-		botColor = powered ? packMacColorBG(_macColors[kMcLwall].fg) : topColor;
+		botColor = powered ? packMacColor(_macColors[kMcLwall].fg) : topColor;
 	}
 
 	const bool keyChanged = !_animPatternValid
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 3482d847f85..073cfd73f09 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -34,6 +34,7 @@
 #include "common/rendermode.h"
 #include "engines/advancedDetector.h"
 #include "engines/engine.h"
+#include "graphics/pixelformat.h"
 
 namespace Common {
 class MacResManager;
@@ -55,6 +56,24 @@ namespace Colony {
 class Renderer;
 class Sound;
 
+// Engine-wide color packing. The OpenGL renderer's useColor() treats values
+// with the high byte == 0xFF as direct ARGB (R=bits 16-23, G=8-15, B=0-7) and
+// values with high byte 0 as palette indices. The PixelFormat below matches
+// that direct-ARGB layout exactly so we can build colors via ARGBToColor.
+inline const Graphics::PixelFormat &renderColorFormat() {
+	static const Graphics::PixelFormat fmt(4, 8, 8, 8, 8, 16, 8, 0, 24);
+	return fmt;
+}
+
+inline uint32 packRGB(byte r, byte g, byte b) {
+	return renderColorFormat().ARGBToColor(255, r, g, b);
+}
+
+// Mac native QuickDraw stores RGB as 16-bit-per-channel; we collapse to 8 bits.
+inline uint32 packMacColor(const uint16 rgb[3]) {
+	return packRGB((byte)(rgb[0] >> 8), (byte)(rgb[1] >> 8), (byte)(rgb[2] >> 8));
+}
+
 enum ColonyAction {
 	kActionNone,
 	kActionMoveForward,
diff --git a/engines/colony/intro.cpp b/engines/colony/intro.cpp
index 020b284e740..e09190b446e 100644
--- a/engines/colony/intro.cpp
+++ b/engines/colony/intro.cpp
@@ -1125,7 +1125,7 @@ bool ColonyEngine::drawPict(int resID) {
 						uint32 pixel = surface->getPixel(ix, iy);
 						surface->format.colorToRGB(pixel, r, g, b);
 					}
-					_gfx->setPixel(sx, sy, 0xFF000000 | ((uint32)r << 16) | ((uint32)g << 8) | b);
+					_gfx->setPixel(sx, sy, packRGB(r, g, b));
 				}
 			}
 			_gfx->copyToScreen();
diff --git a/engines/colony/movement.cpp b/engines/colony/movement.cpp
index 667231e777b..9e7cfcbd87f 100644
--- a/engines/colony/movement.cpp
+++ b/engines/colony/movement.cpp
@@ -63,11 +63,6 @@ const int kTunnelST[] = {
 
 const int kTunnelStraight[60] = {0};
 
-uint32 packTunnelMacColor(const uint16 rgb[3]) {
-	return 0xFF000000 | ((uint32)(rgb[0] >> 8) << 16) |
-		((uint32)(rgb[1] >> 8) << 8) | (uint32)(rgb[2] >> 8);
-}
-
 void fillTunnelPattern(Renderer *gfx, const Common::Rect &rect, uint32 fg, uint32 bg, int pattern) {
 	if (rect.isEmpty())
 		return;
@@ -801,8 +796,8 @@ void ColonyEngine::playTunnelAirlockEffect() {
 	const Common::Rect effectRect(0, _menuBarHeight, _width, _height);
 	const bool macColor = (_renderMode == Common::kRenderMacintosh && _hasMacColors);
 	const int tunnelColor = 24; // c_tunnel
-	const uint32 fillFg = macColor ? packTunnelMacColor(_macColors[tunnelColor].fg) : 0;
-	const uint32 fillBg = macColor ? packTunnelMacColor(_macColors[tunnelColor].bg) : 0;
+	const uint32 fillFg = macColor ? packMacColor(_macColors[tunnelColor].fg) : 0;
+	const uint32 fillBg = macColor ? packMacColor(_macColors[tunnelColor].bg) : 0;
 	const uint32 lineColor = macColor ? 0xFF000000 : 15;
 	int troy = 180;
 	int counter = 4;
@@ -899,8 +894,8 @@ void ColonyEngine::playTunnelEffect(bool falling) {
 	const bool macColor = (_renderMode == Common::kRenderMacintosh && _hasMacColors);
 	const int tunnelColor = 24; // c_tunnel
 	const int tunnelFrames = falling ? 10 : 49;
-	const uint32 fillFg = macColor ? packTunnelMacColor(_macColors[tunnelColor].fg) : 0;
-	const uint32 fillBg = macColor ? packTunnelMacColor(_macColors[tunnelColor].bg) : 0;
+	const uint32 fillFg = macColor ? packMacColor(_macColors[tunnelColor].fg) : 0;
+	const uint32 fillBg = macColor ? packMacColor(_macColors[tunnelColor].bg) : 0;
 	const uint32 lineColor = macColor ? 0xFF000000 : 15;
 	int troy = 180;
 	int cnt = 0;
diff --git a/engines/colony/render_internal.h b/engines/colony/render_internal.h
index b19de4098a4..0af6bb1b877 100644
--- a/engines/colony/render_internal.h
+++ b/engines/colony/render_internal.h
@@ -100,11 +100,6 @@ static const byte *kMacStippleData[] = {
 	nullptr         // kPatternClear  - outline only
 };
 
-// Pack Mac 16-bit RGB into 32-bit ARGB with 0xFF000000 marker for direct RGB.
-inline uint32 packMacColor(const uint16 rgb[3]) {
-	return 0xFF000000 | ((rgb[0] >> 8) << 16) | ((rgb[1] >> 8) << 8) | (rgb[2] >> 8);
-}
-
 // Helper: set up Mac color stipple rendering for a given cColor[] pattern.
 // Configures setMacColors/setStippleData/setWireframe for the pattern type.
 // Returns the stipple pointer (null for solid patterns)  caller must clear with setStippleData(nullptr).
diff --git a/engines/colony/ui.cpp b/engines/colony/ui.cpp
index e0141e5cc75..1ee16d7374f 100644
--- a/engines/colony/ui.cpp
+++ b/engines/colony/ui.cpp
@@ -45,16 +45,6 @@
 
 namespace Colony {
 
-// Pack RGB into 32-bit ARGB with 0xFF000000 marker for direct RGB rendering.
-uint32 packRGB(byte r, byte g, byte b) {
-	return 0xFF000000 | ((uint32)r << 16) | ((uint32)g << 8) | b;
-}
-
-// Pack Mac 16-bit RGB into 32-bit ARGB.
-uint32 packMacColorUI(const uint16 rgb[3]) {
-	return 0xFF000000 | ((rgb[0] >> 8) << 16) | ((rgb[1] >> 8) << 8) | (rgb[2] >> 8);
-}
-
 bool drawMacTextPopup(Graphics::MacWindowManager *wm, Renderer *gfx,
 		int screenWidth, int screenHeight, int centerX, int centerY,
 		const Common::Array<Common::String> &lines, Graphics::TextAlign align, bool macColor) {
@@ -545,7 +535,7 @@ void ColonyEngine::drawDashboardMac() {
 	const bool macColor = _hasMacColors;
 	const uint32 colBlack = packRGB(0, 0, 0);
 	const uint32 colWhite = packRGB(255, 255, 255);
-	const uint32 colWinBg = macColor ? packMacColorUI(_macColors[7].bg) : colWhite;
+	const uint32 colWinBg = macColor ? packMacColor(_macColors[7].bg) : colWhite;
 	// power.c: ForeColor(blueColor)  on 1-bit display, blue maps to black
 	const uint32 colBlue = macColor ? packRGB(0, 0, 255) : colBlack;
 


Commit: d4a9b8612595323d997348b8ee341f708e062b85
    https://github.com/scummvm/scummvm/commit/d4a9b8612595323d997348b8ee341f708e062b85
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-30T10:16:05+02:00

Commit Message:
COLONY: use managed surfaces to increase performance

Changed paths:
    engines/colony/animation.cpp
    engines/colony/colony.h
    engines/colony/intro.cpp
    engines/colony/ui.cpp


diff --git a/engines/colony/animation.cpp b/engines/colony/animation.cpp
index 05436e4cbe0..970fe084230 100644
--- a/engines/colony/animation.cpp
+++ b/engines/colony/animation.cpp
@@ -371,6 +371,12 @@ void ColonyEngine::deleteAnimation() {
 	_backgroundMask = nullptr;
 	delete _backgroundFG;
 	_backgroundFG = nullptr;
+	if (_backgroundBaked) {
+		_backgroundBaked->free();
+		delete _backgroundBaked;
+		_backgroundBaked = nullptr;
+	}
+	_backgroundBakedKey = 0;
 	for (uint i = 0; i < _cSprites.size(); i++)
 		delete _cSprites[i];
 	_cSprites.clear();
@@ -799,7 +805,7 @@ void ColonyEngine::drawAnimation() {
 		}
 		drawAnimationImage(_backgroundFG, _backgroundMask,
 			ox + _backgroundLocate.left, oy + _backgroundLocate.top,
-			bgFill);
+			bgFill, _backgroundBaked, _backgroundBakedKey);
 	}
 
 	// Draw complex sprites
@@ -838,54 +844,73 @@ void ColonyEngine::drawComplexSprite(int index, int ox, int oy) {
 			fillColor = resolveAnimColor(0); // fallback to level-based
 	}
 
-	drawAnimationImage(s->fg, s->mask, x, y, fillColor);
+	drawAnimationImage(s->fg, s->mask, x, y, fillColor, s->baked, s->bakedKey);
 }
 
-void ColonyEngine::drawAnimationImage(Image *img, Image *mask, int x, int y, uint32 fillColor) {
+void ColonyEngine::drawAnimationImage(Image *img, Image *mask, int x, int y, uint32 fillColor,
+		Graphics::Surface *&bakedCache, uint64 &bakedCacheKey) {
 	if (!img || !img->data)
 		return;
 
-	const bool useColor = (_hasMacColors && _renderMode == Common::kRenderMacintosh);
 	// Mac QuickDraw srcBic+srcOr rendering:
 	//   mask bit=1 -> opaque (part of sprite)
 	//   fg bit=1   -> ForeColor (black)
 	//   fg bit=0   -> BackColor (fillColor from BMColor)
 	// Mac B&W: same — fg bit=1 is black (0), fg bit=0 is white (15).
 	// DOS MetaWINDOW: OPPOSITE — fg bit=1 is white (15), fg bit=0 is black (0).
+	const bool useColor = (_hasMacColors && _renderMode == Common::kRenderMacintosh);
 	const bool isMacMode = (_renderMode == Common::kRenderMacintosh);
-	uint32 fgColor, bgColor;
+
+	// Pixels written into the alpha-keyed RGBA cache. mask=0 → alpha 0
+	// (transparent), so drawSurface's alpha-blend skips them naturally.
+	const Graphics::PixelFormat fmt(4, 8, 8, 8, 8, 24, 16, 8, 0);
+	const uint32 black = fmt.ARGBToColor(255, 0, 0, 0);
+	const uint32 white = fmt.ARGBToColor(255, 255, 255, 255);
+	const uint32 transparent = 0;
+
+	uint32 fgPixel, bgPixel;
 	if (useColor) {
-		fgColor = (uint32)0xFF000000;
-		bgColor = fillColor;
+		fgPixel = black;
+		bgPixel = fmt.ARGBToColor(255,
+			(fillColor >> 16) & 0xFF, (fillColor >> 8) & 0xFF, fillColor & 0xFF);
 	} else if (isMacMode) {
-		fgColor = 0;
-		bgColor = 15;
+		fgPixel = black;
+		bgPixel = white;
 	} else {
-		fgColor = 15;
-		bgColor = 0;
+		fgPixel = white;
+		bgPixel = black;
 	}
 
-	for (int iy = 0; iy < img->height; iy++) {
-		for (int ix = 0; ix < img->width; ix++) {
-			int byteIdx = iy * img->rowBytes + (ix / 8);
-			int bitIdx = 7 - (ix % 8);
-
-			bool maskSet = true;
-			if (mask && mask->data) {
-				int mByteIdx = iy * mask->rowBytes + (ix / 8);
-				int mBitIdx = 7 - (ix % 8);
-				maskSet = (mask->data[mByteIdx] & (1 << mBitIdx)) != 0;
+	const uint64 key = ((uint64)fgPixel << 32) | bgPixel;
+	if (!bakedCache || bakedCacheKey != key
+			|| bakedCache->w != img->width || bakedCache->h != img->height) {
+		if (bakedCache) {
+			bakedCache->free();
+			delete bakedCache;
+		}
+		bakedCache = new Graphics::Surface();
+		bakedCache->create(img->width, img->height, fmt);
+
+		uint32 *pixels = (uint32 *)bakedCache->getPixels();
+		for (int iy = 0; iy < img->height; iy++) {
+			const byte *fgRow = img->data + iy * img->rowBytes;
+			const byte *maskRow = (mask && mask->data) ? mask->data + iy * mask->rowBytes : nullptr;
+			uint32 *dst = pixels + iy * img->width;
+			for (int ix = 0; ix < img->width; ix++) {
+				const int bitIdx = 7 - (ix & 7);
+				const bool maskSet = !maskRow || ((maskRow[ix >> 3] & (1 << bitIdx)) != 0);
+				if (!maskSet) {
+					dst[ix] = transparent;
+					continue;
+				}
+				const bool fgSet = (fgRow[ix >> 3] & (1 << bitIdx)) != 0;
+				dst[ix] = fgSet ? fgPixel : bgPixel;
 			}
-
-			if (!maskSet)
-				continue;
-
-			bool fgSet = (img->data[byteIdx] & (1 << bitIdx)) != 0;
-			uint32 color = fgSet ? fgColor : bgColor;
-
-			_gfx->setPixel(x + ix, y + iy, color);
 		}
+		bakedCacheKey = key;
 	}
+
+	_gfx->drawSurface(bakedCache, x, y);
 }
 
 Image *ColonyEngine::loadImage(Common::SeekableReadStreamEndian &file) {
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 073cfd73f09..a7f70bc0876 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -35,6 +35,7 @@
 #include "engines/advancedDetector.h"
 #include "engines/engine.h"
 #include "graphics/pixelformat.h"
+#include "graphics/surface.h"
 
 namespace Common {
 class MacResManager;
@@ -48,7 +49,6 @@ class FrameLimiter;
 class MacMenu;
 class MacWindowManager;
 class ManagedSurface;
-struct Surface;
 }
 
 namespace Colony {
@@ -392,8 +392,22 @@ struct Sprite {
 	Common::Rect locate;
 	bool used;
 
-	Sprite() : fg(nullptr), mask(nullptr), used(false) {}
-	~Sprite() { delete fg; delete mask; }
+	// Per-sprite render cache: bit-pattern + mask are baked into an
+	// alpha-keyed RGBA surface and uploaded once via drawSurface, instead
+	// of issuing setPixel per pixel each frame. Invalidated when the
+	// (fgColor, bgColor) pair changes (level/palette transition).
+	Graphics::Surface *baked;
+	uint64 bakedKey;
+
+	Sprite() : fg(nullptr), mask(nullptr), used(false), baked(nullptr), bakedKey(0) {}
+	~Sprite() {
+		delete fg;
+		delete mask;
+		if (baked) {
+			baked->free();
+			delete baked;
+		}
+	}
 };
 
 struct ComplexSprite {
@@ -725,6 +739,10 @@ private:
 	Common::Array<ComplexSprite *> _lSprites;
 	Image *_backgroundMask = nullptr;
 	Image *_backgroundFG = nullptr;
+	// Same render cache convention as Sprite::baked, applied to the
+	// per-animation background image (no Sprite owner, so it lives here).
+	Graphics::Surface *_backgroundBaked = nullptr;
+	uint64 _backgroundBakedKey = 0;
 	Common::Rect _backgroundClip;
 	Common::Rect _backgroundLocate;
 	bool _backgroundActive;
@@ -774,7 +792,8 @@ private:
 	void updateAnimation();
 	void drawAnimation();
 	void drawComplexSprite(int index, int ox, int oy);
-	void drawAnimationImage(Image *img, Image *mask, int x, int y, uint32 fillColor = 0xFFFFFFFF);
+	void drawAnimationImage(Image *img, Image *mask, int x, int y, uint32 fillColor,
+			Graphics::Surface *&bakedCache, uint64 &bakedCacheKey);
 	uint32 resolveAnimColor(int16 bmEntry) const;
 	Image *loadImage(Common::SeekableReadStreamEndian &file);
 	void unpackBytes(Common::SeekableReadStreamEndian &file, byte *dst, uint32 len);
diff --git a/engines/colony/intro.cpp b/engines/colony/intro.cpp
index e09190b446e..eee3ebe8dc5 100644
--- a/engines/colony/intro.cpp
+++ b/engines/colony/intro.cpp
@@ -971,12 +971,11 @@ bool ColonyEngine::timeSquare(const Common::String &str, const Graphics::Font *m
 			_gfx->setPalette(pal, textIndex, 1);
 		}
 
-		// Draw the blue gradient lines above/below the center band.
+		// Draw the blue gradient bands above/below the center band — each
+		// iteration is a 2-pixel-tall stripe in palette index 160+i.
 		for (int i = 0; i < 16; i++) {
-			_gfx->drawLine(0, centery - 2 - i * 2, _width, centery - 2 - i * 2, 160 + i);
-			_gfx->drawLine(0, centery - 2 - (i * 2 + 1), _width, centery - 2 - (i * 2 + 1), 160 + i);
-			_gfx->drawLine(0, centery + 16 + i * 2, _width, centery + 16 + i * 2, 160 + i);
-			_gfx->drawLine(0, centery + 16 + i * 2 + 1, _width, centery + 16 + i * 2 + 1, 160 + i);
+			_gfx->fillRect(Common::Rect(0, centery - 3 - i * 2, _width, centery - 1 - i * 2), 160 + i);
+			_gfx->fillRect(Common::Rect(0, centery + 16 + i * 2, _width, centery + 18 + i * 2), 160 + i);
 		}
 	} else {
 		// DOS warning band: white outer lines, gray inner lines, white text.
diff --git a/engines/colony/ui.cpp b/engines/colony/ui.cpp
index 1ee16d7374f..f82440cd37f 100644
--- a/engines/colony/ui.cpp
+++ b/engines/colony/ui.cpp
@@ -233,25 +233,14 @@ Graphics::Surface *ColonyEngine::loadPictSurface(int resID) {
 	return result;
 }
 
-// Draw a PICT surface at a specific destination position using packRGB.
-// Matches original DrawPicture(pic, &rect) where rect is positioned at (destX, destY).
+// Draw a PICT surface at a specific destination position. The surface was
+// created by loadPictSurface() in the exact ARGB layout the renderer's
+// drawSurface() consumes, so this is a single textured-quad blit — no need
+// to walk pixels.
 void ColonyEngine::drawPictAt(Graphics::Surface *surf, int destX, int destY) {
 	if (!surf)
 		return;
-	for (int y = 0; y < surf->h; y++) {
-		int sy = destY + y;
-		if (sy < 0 || sy >= _height)
-			continue;
-		for (int x = 0; x < surf->w; x++) {
-			int sx = destX + x;
-			if (sx < 0 || sx >= _width)
-				continue;
-			uint32 pixel = surf->getPixel(x, y);
-			byte a, r, g, b;
-			surf->format.colorToARGB(pixel, a, r, g, b);
-			_gfx->setPixel(sx, sy, packRGB(r, g, b));
-		}
-	}
+	_gfx->drawSurface(surf, destX, destY);
 }
 
 void ColonyEngine::updateViewportLayout() {




More information about the Scummvm-git-logs mailing list