[Scummvm-git-logs] scummvm master -> 732a19c1eaece169ba0ddab963fcf9a949675f73

bluegr noreply at scummvm.org
Mon Nov 17 12:43:00 UTC 2025


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

Summary:
e810fd9fcf GRAPHICS: Extend Surface::clip to support flipping
1c25b17517 GRAPHICS: Add generic alpha blitting routines
18e4df9a8f GRAPHICS: Ensure that ManagedSurface transparent colours are converted
8ebbcb193a NGI: Replace per-pixel alpha with masks and colour keys
732a19c1ea NGI: Support arbitrary pixel formats


Commit: e810fd9fcfd4368e5630ac2b990935a6850b0919
    https://github.com/scummvm/scummvm/commit/e810fd9fcfd4368e5630ac2b990935a6850b0919
Author: Cameron Cawley (ccawley2011 at gmail.com)
Date: 2025-11-17T14:42:55+02:00

Commit Message:
GRAPHICS: Extend Surface::clip to support flipping

Changed paths:
    graphics/managed_surface.h
    graphics/surface.cpp
    graphics/surface.h


diff --git a/graphics/managed_surface.h b/graphics/managed_surface.h
index 6694a0597a2..292e4442218 100644
--- a/graphics/managed_surface.h
+++ b/graphics/managed_surface.h
@@ -123,8 +123,8 @@ public:
 	/**
 	 * Clip the given source bounds so the passed destBounds will be entirely on-screen.
 	 */
-	bool clip(Common::Rect& srcBounds, Common::Rect& destBounds) const {
-		return _innerSurface.clip(srcBounds, destBounds);
+	bool clip(Common::Rect& srcBounds, Common::Rect& destBounds, uint src_w = 0, uint src_h = 0, byte flip = FLIP_NONE) const {
+		return _innerSurface.clip(srcBounds, destBounds, src_w, src_h, flip);
 	}
 
 public:
diff --git a/graphics/surface.cpp b/graphics/surface.cpp
index 9908d3100c0..e6bd8c04240 100644
--- a/graphics/surface.cpp
+++ b/graphics/surface.cpp
@@ -212,7 +212,7 @@ const Surface Surface::getSubArea(const Common::Rect &area) const {
 	return subSurface;
 }
 
-bool Surface::clip(Common::Rect &srcBounds, Common::Rect &destBounds) const {
+bool Surface::clip(Common::Rect &srcBounds, Common::Rect &destBounds, uint src_w, uint src_h, byte flip) const {
 	if (destBounds.left >= this->w || destBounds.top >= this->h ||
 		destBounds.right <= 0 || destBounds.bottom <= 0)
 		return false;
@@ -238,6 +238,18 @@ bool Surface::clip(Common::Rect &srcBounds, Common::Rect &destBounds) const {
 		destBounds.left = 0;
 	}
 
+	if (flip & FLIP_H) {
+		int tmp_w = srcBounds.width();
+		srcBounds.left = src_w - srcBounds.right;
+		srcBounds.right = srcBounds.left + tmp_w;
+	}
+
+	if (flip & FLIP_V) {
+		int tmp_h = srcBounds.height();
+		srcBounds.top = src_h - srcBounds.bottom;
+		srcBounds.bottom = srcBounds.top + tmp_h;
+	}
+
 	return true;
 }
 
diff --git a/graphics/surface.h b/graphics/surface.h
index 0d9b32d47a7..84ed3a4682b 100644
--- a/graphics/surface.h
+++ b/graphics/surface.h
@@ -291,7 +291,7 @@ public:
 	/**
 	 * Clip the given source bounds so the passed destBounds will be entirely on-screen.
 	 */
-	bool clip(Common::Rect &srcBounds, Common::Rect &destBounds) const;
+	bool clip(Common::Rect &srcBounds, Common::Rect &destBounds, uint src_w = 0, uint src_h = 0, byte flip = FLIP_NONE) const;
 
 	/**
 	 * Copy a bitmap to the internal buffer of the surface.


Commit: 1c25b175171ef2c46c7fa83481e8b2fbcd6d71e4
    https://github.com/scummvm/scummvm/commit/1c25b175171ef2c46c7fa83481e8b2fbcd6d71e4
Author: Cameron Cawley (ccawley2011 at gmail.com)
Date: 2025-11-17T14:42:55+02:00

Commit Message:
GRAPHICS: Add generic alpha blitting routines

Changed paths:
    engines/freescape/freescape.cpp
    engines/testbed/image.cpp
    engines/vcruise/runtime.cpp
    graphics/blit.h
    graphics/blit/blit-alpha.cpp
    graphics/managed_surface.cpp
    graphics/managed_surface.h


diff --git a/engines/freescape/freescape.cpp b/engines/freescape/freescape.cpp
index 6909f2850d5..920c511f53f 100644
--- a/engines/freescape/freescape.cpp
+++ b/engines/freescape/freescape.cpp
@@ -1245,7 +1245,7 @@ Graphics::ManagedSurface *FreescapeEngine::loadAndConvertScrImage(Common::Seekab
 	Graphics::ManagedSurface *surface = new Graphics::ManagedSurface();
 	const Graphics::Surface *decoded = decoder.getSurface();
 	surface->create(320, 200, _gfx->_texturePixelFormat);
-	surface->simpleBlitFrom(*decoded, Common::Point((320 - decoded->w) / 2, (200 - decoded->h) / 2), &decoder.getPalette());
+	surface->simpleBlitFrom(*decoded, Common::Point((320 - decoded->w) / 2, (200 - decoded->h) / 2), Graphics::FLIP_NONE, false, 255, &decoder.getPalette());
 	return surface;
 }
 
diff --git a/engines/testbed/image.cpp b/engines/testbed/image.cpp
index 848f10bbd6c..05d61cc6add 100644
--- a/engines/testbed/image.cpp
+++ b/engines/testbed/image.cpp
@@ -350,7 +350,7 @@ bool ImageTests::testImageDecoder(Common::Path &filepath, Image::ImageDecoder &d
 
 	Graphics::Screen screen;
 	if (decoder.hasPalette()) {
-		screen.simpleBlitFrom(*pSurface, &decoder.getPalette());
+		screen.simpleBlitFrom(*pSurface, Graphics::FLIP_NONE, false, 255, &decoder.getPalette());
 	} else {
 		screen.simpleBlitFrom(*pSurface);
 	}
diff --git a/engines/vcruise/runtime.cpp b/engines/vcruise/runtime.cpp
index 1fe6edc135b..f47f8bcb6a4 100644
--- a/engines/vcruise/runtime.cpp
+++ b/engines/vcruise/runtime.cpp
@@ -2725,7 +2725,7 @@ void Runtime::continuePlayingAnimation(bool loop, bool useStopFrame, bool &outAn
 
 		if (copyRect.isValidRect() || !copyRect.isEmpty()) {
 			Graphics::Palette p(_animDecoder->getPalette(), 256);
-			_gameSection.surf->simpleBlitFrom(*surface, copyRect, copyRect.origin(), &p);
+			_gameSection.surf->simpleBlitFrom(*surface, copyRect, copyRect.origin(), Graphics::FLIP_NONE, false, 255, &p);
 			drawSectionToScreen(_gameSection, copyRect);
 		}
 
diff --git a/graphics/blit.h b/graphics/blit.h
index 147de950a50..a18ed55f858 100644
--- a/graphics/blit.h
+++ b/graphics/blit.h
@@ -210,6 +210,42 @@ bool crossMaskBlitMap(byte *dst, const byte *src, const byte *mask,
 			   const uint w, const uint h,
 			   const uint bytesPerPixel, const uint32 *map);
 
+bool alphaBlit(byte *dst, const byte *src,
+			   const uint dstPitch, const uint srcPitch,
+			   const uint w, const uint h,
+			   const Graphics::PixelFormat &dstFmt, const Graphics::PixelFormat &srcFmt,
+			   const byte flip, const byte aMod);
+
+bool alphaKeyBlit(byte *dst, const byte *src,
+			   const uint dstPitch, const uint srcPitch,
+			   const uint w, const uint h,
+			   const Graphics::PixelFormat &dstFmt, const Graphics::PixelFormat &srcFmt,
+			   const uint32 key, const byte flip, const byte aMod);
+
+bool alphaMaskBlit(byte *dst, const byte *src, const byte *mask,
+			   const uint dstPitch, const uint srcPitch, const uint maskPitch,
+			   const uint w, const uint h,
+			   const Graphics::PixelFormat &dstFmt, const Graphics::PixelFormat &srcFmt,
+			   const byte flip, const byte aMod);
+
+bool alphaBlitMap(byte *dst, const byte *src,
+			   const uint dstPitch, const uint srcPitch,
+			   const uint w, const uint h,
+			   const Graphics::PixelFormat &dstFmt, const uint32 *map,
+			   const byte flip, const byte aMod);
+
+bool alphaKeyBlitMap(byte *dst, const byte *src,
+			   const uint dstPitch, const uint srcPitch,
+			   const uint w, const uint h,
+			   const Graphics::PixelFormat &dstFmt, const uint32 *map,
+			   const uint32 key, const byte flip, const byte aMod);
+
+bool alphaMaskBlitMap(byte *dst, const byte *src, const byte *mask,
+			   const uint dstPitch, const uint srcPitch, const uint maskPitch,
+			   const uint w, const uint h,
+			   const Graphics::PixelFormat &dstFmt, const uint32 *map,
+			   const byte flip, const byte aMod);
+
 typedef void (*FastBlitFunc)(byte *, const byte *, const uint, const uint, const uint, const uint);
 
 #ifdef SCUMMVM_NEON
diff --git a/graphics/blit/blit-alpha.cpp b/graphics/blit/blit-alpha.cpp
index e399b9da553..8e7c998072b 100644
--- a/graphics/blit/blit-alpha.cpp
+++ b/graphics/blit/blit-alpha.cpp
@@ -27,6 +27,282 @@ namespace Graphics {
 
 namespace {
 
+template<typename Color, int Size>
+static inline uint32 READ_PIXEL(const byte *src) {
+	if (Size == sizeof(Color)) {
+		return *(const Color *)src;
+	} else {
+		uint32 color;
+		uint8 *col = (uint8 *)&color;
+#ifdef SCUMM_BIG_ENDIAN
+		if (Size == 3)
+			col++;
+#endif
+		memcpy(col, src, Size);
+		return color;
+	}
+}
+
+template<typename Color, int Size>
+static inline void WRITE_PIXEL(byte *dst, const uint32 color) {
+	if (Size == sizeof(Color)) {
+		*(Color *)dst = color;
+	} else {
+		const uint8 *col = (const uint8 *)&color;
+#ifdef SCUMM_BIG_ENDIAN
+		if (Size == 3)
+			col++;
+#endif
+		memcpy(dst, col, Size);
+	}
+}
+
+template<typename SrcColor, int SrcSize, typename DstColor, int DstSize, bool hasKey, bool hasMask, bool hasMap>
+static inline void alphaBlitLogic(byte *dst, const byte *src, const byte *mask, const uint w, const uint h,
+						const PixelFormat &srcFmt, const PixelFormat &dstFmt, const uint32 *map,
+						const int srcDelta, const int dstDelta, const int maskDelta,
+						const int srcInc, const int dstInc, const int maskInc,
+						const uint32 key, const byte flip, const byte aMod) {
+	const uint32 alphaMask = srcFmt.ARGBToColor(255, 0, 0, 0);
+	const bool convert = hasMap ? false : ((SrcSize != DstSize) ? true : srcFmt == dstFmt);
+
+	for (uint y = 0; y < h; ++y) {
+		for (uint x = 0; x < w; ++x) {
+			const uint32 srcColor = hasMap ? map[*src]
+				: READ_PIXEL<SrcColor, SrcSize>(src);
+
+			const bool isOpaque = hasMask ? (*mask == 0xff)
+				: (hasKey ? (READ_PIXEL<SrcColor, SrcSize>(src) != key)
+				: !alphaMask || ((srcColor & alphaMask) == alphaMask));
+			const bool isTransparent = hasMask ? (*mask == 0x00)
+				: (hasKey ? (READ_PIXEL<SrcColor, SrcSize>(src) == key)
+				: alphaMask && ((srcColor & alphaMask) == 0));
+
+			if (isOpaque && aMod == 0xff) {
+				if (convert) {
+					byte sR, sG, sB;
+					srcFmt.colorToRGB(srcColor, sR, sG, sB);
+					WRITE_PIXEL<DstColor, DstSize>(dst, dstFmt.RGBToColor(sR, sG, sB));
+				} else {
+					WRITE_PIXEL<DstColor, DstSize>(dst, srcColor);
+				}
+			} else if (!isTransparent) {
+				// TODO: Optimise for matching formats?
+				const uint32 dstColor = READ_PIXEL<DstColor, DstSize>(dst);
+
+				byte sA, sR, sG, sB;
+				srcFmt.colorToARGB(srcColor, sA, sR, sG, sB);
+
+				byte dR, dG, dB;
+				dstFmt.colorToRGB(dstColor, dR, dG, dB);
+
+				if (hasKey)
+					sA = aMod;
+				else if (hasMask)
+					sA = ((*mask * aMod) >> 8);
+				else
+					sA = ((sA * aMod) >> 8);
+
+				dR = (dR * (255-sA) + sR * sA) >> 8;
+				dG = (dG * (255-sA) + sG * sA) >> 8;
+				dB = (dB * (255-sA) + sB * sA) >> 8;
+
+				const uint32 outColor = dstFmt.RGBToColor(dR, dG, dB);
+				WRITE_PIXEL<DstColor, DstSize>(dst, outColor);
+			}
+
+			src += srcInc;
+			dst += dstInc;
+			if (hasMask)
+				mask += maskInc;
+		}
+
+		src += srcDelta;
+		dst += dstDelta;
+		if (hasMask)
+			mask += maskDelta;
+	}
+}
+
+template<bool hasKey, bool hasMask>
+static inline bool alphaBlitHelper(byte *dst, const byte *src, const byte *mask, const uint w, const uint h,
+                         const PixelFormat &srcFmt, const PixelFormat &dstFmt,
+                         const uint srcPitch, const uint dstPitch, const uint maskPitch,
+                         const uint32 key, const byte flip, const byte aMod) {
+	const bool hasMap = false;
+	const bool flipx = flip & FLIP_H;
+	const bool flipy = flip & FLIP_V;
+
+	// Faster, but larger, to provide optimized handling for each case.
+	      int dstDelta = (dstPitch - w * dstFmt.bytesPerPixel);
+	const int srcDelta = (srcPitch - w * srcFmt.bytesPerPixel);
+	const int maskDelta = hasMask ? (maskPitch - w) : 0;
+
+	const int dstInc = flipx ? -dstFmt.bytesPerPixel : dstFmt.bytesPerPixel;
+	const int srcInc = srcFmt.bytesPerPixel;
+	const int maskInc = 1;
+
+	if (flipx)
+		dst += (w - 1) * dstFmt.bytesPerPixel;
+
+	if (flipy)
+		dst += (h - 1) * dstPitch;
+
+	if (flipy && flipx)
+		dstDelta = -dstDelta;
+	else if (flipy)
+		dstDelta = -((dstPitch * 2) - dstDelta);
+	else if (flipx)
+		dstDelta =   (dstPitch * 2) - dstDelta;
+
+	if (aMod == 0)
+		return true;
+
+	// TODO: optimized cases for dstDelta of 0
+	if (dstFmt.bytesPerPixel == 2) {
+		if (srcFmt.bytesPerPixel == 2) {
+			alphaBlitLogic<uint16, 2, uint16, 2, hasKey, hasMask, hasMap>(dst, src, mask, w, h, srcFmt, dstFmt, nullptr, srcDelta, dstDelta, maskDelta, srcInc, dstInc, maskInc, key, flip, aMod);
+		} else if (srcFmt.bytesPerPixel == 3) {
+			alphaBlitLogic<uint8,  3, uint16, 2, hasKey, hasMask, hasMap>(dst, src, mask, w, h, srcFmt, dstFmt, nullptr, srcDelta, dstDelta, maskDelta, srcInc, dstInc, maskInc, key, flip, aMod);
+		} else {
+			alphaBlitLogic<uint32, 4, uint16, 2, hasKey, hasMask, hasMap>(dst, src, mask, w, h, srcFmt, dstFmt, nullptr, srcDelta, dstDelta, maskDelta, srcInc, dstInc, maskInc, key, flip, aMod);
+		}
+	} else if (dstFmt.bytesPerPixel == 4) {
+		if (srcFmt.bytesPerPixel == 2) {
+			alphaBlitLogic<uint16, 2, uint32, 4, hasKey, hasMask, hasMap>(dst, src, mask, w, h, srcFmt, dstFmt, nullptr, srcDelta, dstDelta, maskDelta, srcInc, dstInc, maskInc, key, flip, aMod);
+		} else if (srcFmt.bytesPerPixel == 3) {
+			alphaBlitLogic<uint8,  3, uint32, 4, hasKey, hasMask, hasMap>(dst, src, mask, w, h, srcFmt, dstFmt, nullptr, srcDelta, dstDelta, maskDelta, srcInc, dstInc, maskInc, key, flip, aMod);
+		} else {
+			alphaBlitLogic<uint32, 4, uint32, 4, hasKey, hasMask, hasMap>(dst, src, mask, w, h, srcFmt, dstFmt, nullptr, srcDelta, dstDelta, maskDelta, srcInc, dstInc, maskInc, key, flip, aMod);
+		}
+	} else {
+		return false;
+	}
+	return true;
+}
+
+template<bool hasKey, bool hasMask>
+static inline bool alphaBlitMapHelper(byte *dst, const byte *src, const byte *mask, const uint w, const uint h,
+                         const PixelFormat &dstFmt, const uint32 *map,
+                         const uint srcPitch, const uint dstPitch, const uint maskPitch,
+                         const uint32 key, const byte flip, const byte aMod) {
+	const Graphics::PixelFormat &srcFmt = dstFmt;
+	const bool hasMap = true;
+	const bool flipx = flip & FLIP_H;
+	const bool flipy = flip & FLIP_V;
+
+	// Faster, but larger, to provide optimized handling for each case.
+	      int dstDelta = (dstPitch - w * dstFmt.bytesPerPixel);
+	const int srcDelta = (srcPitch - w);
+	const int maskDelta = hasMask ? (maskPitch - w) : 0;
+
+	const int dstInc = flipx ? -dstFmt.bytesPerPixel : dstFmt.bytesPerPixel;
+	const int srcInc = 1;
+	const int maskInc = 1;
+
+	if (flipx)
+		dst += (w - 1) * dstFmt.bytesPerPixel;
+
+	if (flipy)
+		dst += (h - 1) * dstPitch;
+
+	if (flipy && flipx)
+		dstDelta = -dstDelta;
+	else if (flipy)
+		dstDelta = -((dstPitch * 2) - dstDelta);
+	else if (flipx)
+		dstDelta =   (dstPitch * 2) - dstDelta;
+
+	// TODO: optimized cases for dstDelta of 0
+	if (dstFmt.bytesPerPixel == 2) {
+		alphaBlitLogic<uint8,  1, uint16, 2, hasKey, hasMask, hasMap>(dst, src, mask, w, h, srcFmt, dstFmt, map, srcDelta, dstDelta, maskDelta, srcInc, dstInc, maskInc, key, flip, aMod);
+	} else if (dstFmt.bytesPerPixel == 4) {
+		alphaBlitLogic<uint8,  1, uint32, 4, hasKey, hasMask, hasMap>(dst, src, mask, w, h, srcFmt, dstFmt, map, srcDelta, dstDelta, maskDelta, srcInc, dstInc, maskInc, key, flip, aMod);
+	} else {
+		return false;
+	}
+	return true;
+}
+
+} // End of anonymous namespace
+
+bool alphaBlit(byte *dst, const byte *src,
+				const uint dstPitch, const uint srcPitch,
+				const uint w, const uint h,
+				const Graphics::PixelFormat &dstFmt, const Graphics::PixelFormat &srcFmt,
+				const byte flip, const byte aMod) {
+	// Error out if conversion is impossible
+	if ((srcFmt.bytesPerPixel == 1) || (dstFmt.bytesPerPixel == 1)
+			|| (!srcFmt.bytesPerPixel) || (!dstFmt.bytesPerPixel))
+		return false;
+
+	return alphaBlitHelper<false, false>(dst, src, nullptr, w, h, srcFmt, dstFmt, srcPitch, dstPitch, 0, 0, flip, aMod);
+}
+
+bool alphaKeyBlit(byte *dst, const byte *src,
+				const uint dstPitch, const uint srcPitch,
+				const uint w, const uint h,
+				const Graphics::PixelFormat &dstFmt, const Graphics::PixelFormat &srcFmt,
+				const uint32 key, const byte flip, const byte aMod) {
+	// Error out if conversion is impossible
+	if ((srcFmt.bytesPerPixel == 1) || (dstFmt.bytesPerPixel == 1)
+			|| (!srcFmt.bytesPerPixel) || (!dstFmt.bytesPerPixel))
+		return false;
+
+	return alphaBlitHelper<true, false>(dst, src, nullptr, w, h, srcFmt, dstFmt, srcPitch, dstPitch, 0, key, flip, aMod);
+}
+
+bool alphaMaskBlit(byte *dst, const byte *src, const byte *mask,
+				const uint dstPitch, const uint srcPitch, const uint maskPitch,
+				const uint w, const uint h,
+				const Graphics::PixelFormat &dstFmt, const Graphics::PixelFormat &srcFmt,
+				const byte flip, const byte aMod) {
+	// Error out if conversion is impossible
+	if ((srcFmt.bytesPerPixel == 1) || (dstFmt.bytesPerPixel == 1)
+			|| (!srcFmt.bytesPerPixel) || (!dstFmt.bytesPerPixel))
+		return false;
+
+	return alphaBlitHelper<false, true>(dst, src, mask, w, h, srcFmt, dstFmt, srcPitch, dstPitch, maskPitch, 0, flip, aMod);
+}
+
+bool alphaBlitMap(byte *dst, const byte *src,
+			        const uint dstPitch, const uint srcPitch,
+			        const uint w, const uint h,
+			        const Graphics::PixelFormat &dstFmt, const uint32 *map,
+			        const byte flip, const byte aMod) {
+	// Error out if conversion is impossible
+	if ((dstFmt.bytesPerPixel == 1) || (!dstFmt.bytesPerPixel))
+		return false;
+
+	return alphaBlitMapHelper<false, false>(dst, src, nullptr, w, h, dstFmt, map, srcPitch, dstPitch, 0, 0, flip, aMod);
+}
+
+bool alphaKeyBlitMap(byte *dst, const byte *src,
+			        const uint dstPitch, const uint srcPitch,
+			        const uint w, const uint h,
+			        const Graphics::PixelFormat &dstFmt, const uint32 *map,
+			        const uint32 key, const byte flip, const byte aMod) {
+	// Error out if conversion is impossible
+	if ((dstFmt.bytesPerPixel == 1) || (!dstFmt.bytesPerPixel))
+		return false;
+
+	return alphaBlitMapHelper<true, false>(dst, src, nullptr, w, h, dstFmt, map, srcPitch, dstPitch, 0, key, flip, aMod);
+}
+
+bool alphaMaskBlitMap(byte *dst, const byte *src, const byte *mask,
+			        const uint dstPitch, const uint srcPitch, const uint maskPitch,
+			        const uint w, const uint h,
+			        const Graphics::PixelFormat &dstFmt, const uint32 *map,
+			        const byte flip, const byte aMod) {
+	// Error out if conversion is impossible
+	if ((dstFmt.bytesPerPixel == 1) || (!dstFmt.bytesPerPixel))
+		return false;
+
+	return alphaBlitMapHelper<false, true>(dst, src, mask, w, h, dstFmt, map, srcPitch, dstPitch, maskPitch, 0, flip, aMod);
+}
+
+namespace {
+
 template<typename Size, bool overwriteAlpha>
 inline bool applyColorKeyLogic(byte *dst, const byte *src, const uint w, const uint h,
 							   const uint srcDelta, const uint dstDelta,
diff --git a/graphics/managed_surface.cpp b/graphics/managed_surface.cpp
index fb65c46c1ff..f076ca8acb6 100644
--- a/graphics/managed_surface.cpp
+++ b/graphics/managed_surface.cpp
@@ -326,47 +326,60 @@ Graphics::ManagedSurface *ManagedSurface::rotoscale(const TransformStruct &trans
 	return target;
 }
 
-void ManagedSurface::simpleBlitFrom(const Surface &src, const Palette *srcPalette) {
-	simpleBlitFrom(src, Common::Rect(0, 0, src.w, src.h), Common::Point(0, 0), srcPalette);
+void ManagedSurface::simpleBlitFrom(const Surface &src,
+		byte flip, bool alpha, byte aMod,
+		const Palette *srcPalette) {
+	simpleBlitFrom(src, Common::Rect(0, 0, src.w, src.h), Common::Point(0, 0), flip, alpha, aMod, srcPalette);
 }
 
-void ManagedSurface::simpleBlitFrom(const Surface &src, const Common::Point &destPos, const Palette *srcPalette) {
-	simpleBlitFrom(src, Common::Rect(0, 0, src.w, src.h), destPos, srcPalette);
+void ManagedSurface::simpleBlitFrom(const Surface &src, const Common::Point &destPos,
+		byte flip, bool alpha, byte aMod,
+		const Palette *srcPalette) {
+	simpleBlitFrom(src, Common::Rect(0, 0, src.w, src.h), destPos, flip, alpha, aMod, srcPalette);
 }
 
 void ManagedSurface::simpleBlitFrom(const Surface &src, const Common::Rect &srcRect,
-		const Common::Point &destPos, const Palette *srcPalette) {
-	simpleBlitFromInner(src, srcRect, destPos, srcPalette, false, 0);
+		const Common::Point &destPos,
+		byte flip, bool alpha, byte aMod,
+		const Palette *srcPalette) {
+	simpleBlitFromInner(src, srcRect, destPos, srcPalette, false, 0, flip, alpha, aMod);
 }
 
-void ManagedSurface::simpleBlitFrom(const ManagedSurface &src) {
-	simpleBlitFrom(src, Common::Rect(0, 0, src.w, src.h), Common::Point(0, 0));
+void ManagedSurface::simpleBlitFrom(const ManagedSurface &src,
+		byte flip, bool alpha, byte aMod) {
+	simpleBlitFrom(src, Common::Rect(0, 0, src.w, src.h), Common::Point(0, 0), flip, alpha, aMod);
 }
 
-void ManagedSurface::simpleBlitFrom(const ManagedSurface &src, const Common::Point &destPos) {
-	simpleBlitFrom(src, Common::Rect(0, 0, src.w, src.h), destPos);
+void ManagedSurface::simpleBlitFrom(const ManagedSurface &src, const Common::Point &destPos,
+		byte flip, bool alpha, byte aMod) {
+	simpleBlitFrom(src, Common::Rect(0, 0, src.w, src.h), destPos, flip, alpha, aMod);
 }
 
 void ManagedSurface::simpleBlitFrom(const ManagedSurface &src, const Common::Rect &srcRect,
-		const Common::Point &destPos) {
+		const Common::Point &destPos,
+		byte flip, bool alpha, byte aMod) {
 	simpleBlitFromInner(src._innerSurface, srcRect, destPos, src._palette,
-		src._transparentColorSet, src._transparentColor);
+		src._transparentColorSet, src._transparentColor, flip, alpha, aMod);
 }
 
 void ManagedSurface::simpleBlitFromInner(const Surface &src, const Common::Rect &srcRect,
 		const Common::Point &destPos, const Palette *srcPalette,
-		bool transparentColorSet, uint transparentColor) {
+		bool transparentColorSet, uint transparentColor,
+		byte flip, bool alpha, byte aMod) {
+
+	if (aMod == 0)
+		return;
 
 	Common::Rect srcRectC = srcRect;
 	Common::Rect dstRectC = srcRect;
 
 	dstRectC.moveTo(destPos.x, destPos.y);
-	clip(srcRectC, dstRectC);
+	clip(srcRectC, dstRectC, src.w, src.h, flip);
 
 	const byte *srcPtr = (const byte *)src.getBasePtr(srcRectC.left, srcRectC.top);
 	byte *dstPtr = (byte *)getBasePtr(dstRectC.left, dstRectC.top);
 
-	if (format == src.format) {
+	if (format == src.format && !alpha && aMod == 0xff && flip == 0) {
 		if (transparentColorSet) {
 			keyBlit(dstPtr, srcPtr, pitch, src.pitch, srcRectC.width(), srcRectC.height(),
 				format.bytesPerPixel, transparentColor);
@@ -381,70 +394,93 @@ void ManagedSurface::simpleBlitFromInner(const Surface &src, const Common::Rect
 		uint32 map[256];
 		convertPaletteToMap(map, srcPalette->data(), srcPalette->size(), format);
 
-		if (transparentColorSet) {
-			crossKeyBlitMap(dstPtr, srcPtr, pitch, src.pitch, srcRectC.width(), srcRectC.height(),
-				format.bytesPerPixel, map, transparentColor);
+		if (alpha || aMod != 0xff || flip != 0) {
+			if (transparentColorSet) {
+				alphaKeyBlitMap(dstPtr, srcPtr, pitch, src.pitch, srcRectC.width(), srcRectC.height(),
+					format, map, transparentColor, flip, aMod);
+			} else {
+				alphaBlitMap(dstPtr, srcPtr, pitch, src.pitch, srcRectC.width(), srcRectC.height(),
+					format, map, flip, aMod);
+			}
 		} else {
-			crossBlitMap(dstPtr, srcPtr, pitch, src.pitch, srcRectC.width(), srcRectC.height(),
-				format.bytesPerPixel, map);
+			if (transparentColorSet) {
+				crossKeyBlitMap(dstPtr, srcPtr, pitch, src.pitch, srcRectC.width(), srcRectC.height(),
+					format.bytesPerPixel, map, transparentColor);
+			} else {
+				crossBlitMap(dstPtr, srcPtr, pitch, src.pitch, srcRectC.width(), srcRectC.height(),
+					format.bytesPerPixel, map);
+			}
 		}
 	} else {
-		if (transparentColorSet) {
-			crossKeyBlit(dstPtr, srcPtr, pitch, src.pitch, srcRectC.width(), srcRectC.height(),
-				format, src.format, transparentColor);
+		if (alpha || aMod != 0xff || flip != 0) {
+			if (transparentColorSet) {
+				alphaKeyBlit(dstPtr, srcPtr, pitch, src.pitch, srcRectC.width(), srcRectC.height(),
+					format, src.format, transparentColor, flip, aMod);
+			} else {
+				alphaBlit(dstPtr, srcPtr, pitch, src.pitch, srcRectC.width(), srcRectC.height(),
+					format, src.format, flip, aMod);
+			}
 		} else {
-			crossBlit(dstPtr, srcPtr, pitch, src.pitch, srcRectC.width(), srcRectC.height(),
-				format, src.format);
+			if (transparentColorSet) {
+				crossKeyBlit(dstPtr, srcPtr, pitch, src.pitch, srcRectC.width(), srcRectC.height(),
+					format, src.format, transparentColor);
+			} else {
+				crossBlit(dstPtr, srcPtr, pitch, src.pitch, srcRectC.width(), srcRectC.height(),
+					format, src.format);
+			}
 		}
 	}
 
 	addDirtyRect(dstRectC);
 }
 
-void ManagedSurface::maskBlitFrom(const Surface &src, const Surface &mask, const Palette *srcPalette) {
-	maskBlitFrom(src, mask, Common::Rect(0, 0, src.w, src.h), Common::Point(0, 0), srcPalette);
+void ManagedSurface::maskBlitFrom(const Surface &src, const Surface &mask, byte flip, bool alpha, byte aMod, const Palette *srcPalette) {
+	maskBlitFrom(src, mask, Common::Rect(0, 0, src.w, src.h), Common::Point(0, 0), flip, alpha, aMod, srcPalette);
 }
 
-void ManagedSurface::maskBlitFrom(const Surface &src, const Surface &mask, const Common::Point &destPos, const Palette *srcPalette) {
-	maskBlitFrom(src, mask, Common::Rect(0, 0, src.w, src.h), destPos, srcPalette);
+void ManagedSurface::maskBlitFrom(const Surface &src, const Surface &mask, const Common::Point &destPos, byte flip, bool alpha, byte aMod, const Palette *srcPalette) {
+	maskBlitFrom(src, mask, Common::Rect(0, 0, src.w, src.h), destPos, flip, alpha, aMod, srcPalette);
 }
 
 void ManagedSurface::maskBlitFrom(const Surface &src, const Surface &mask, const Common::Rect &srcRect,
-		const Common::Point &destPos, const Palette *srcPalette) {
-	maskBlitFromInner(src, mask, srcRect, destPos, srcPalette);
+		const Common::Point &destPos, byte flip, bool alpha, byte aMod, const Palette *srcPalette) {
+	maskBlitFromInner(src, mask, srcRect, destPos, srcPalette, flip, alpha, aMod);
 }
 
-void ManagedSurface::maskBlitFrom(const ManagedSurface &src, const ManagedSurface &mask) {
-	maskBlitFrom(src, mask, Common::Rect(0, 0, src.w, src.h), Common::Point(0, 0));
+void ManagedSurface::maskBlitFrom(const ManagedSurface &src, const ManagedSurface &mask, byte flip, bool alpha, byte aMod) {
+	maskBlitFrom(src, mask, Common::Rect(0, 0, src.w, src.h), Common::Point(0, 0), flip, alpha, aMod);
 }
 
-void ManagedSurface::maskBlitFrom(const ManagedSurface &src, const ManagedSurface &mask, const Common::Point &destPos) {
-	maskBlitFrom(src, mask, Common::Rect(0, 0, src.w, src.h), destPos);
+void ManagedSurface::maskBlitFrom(const ManagedSurface &src, const ManagedSurface &mask, const Common::Point &destPos, byte flip, bool alpha, byte aMod) {
+	maskBlitFrom(src, mask, Common::Rect(0, 0, src.w, src.h), destPos, flip, alpha, aMod);
 }
 
 void ManagedSurface::maskBlitFrom(const ManagedSurface &src, const ManagedSurface &mask,
-		const Common::Rect &srcRect, const Common::Point &destPos) {
-	maskBlitFromInner(src._innerSurface, mask._innerSurface, srcRect, destPos, src._palette);
+		const Common::Rect &srcRect, const Common::Point &destPos, byte flip, bool alpha, byte aMod) {
+	maskBlitFromInner(src._innerSurface, mask._innerSurface, srcRect, destPos, src._palette, flip, alpha, aMod);
 }
 
 void ManagedSurface::maskBlitFromInner(const Surface &src, const Surface &mask,
 		const Common::Rect &srcRect, const Common::Point &destPos,
-		const Palette *srcPalette) {
+		const Palette *srcPalette, byte flip, bool alpha, byte aMod) {
 
 	if (mask.w != src.w || mask.h != src.h)
 		error("Surface::maskBlitFrom: mask dimensions do not match src");
 
+	if (aMod == 0)
+		return;
+
 	Common::Rect srcRectC = srcRect;
 	Common::Rect dstRectC = srcRect;
 
 	dstRectC.moveTo(destPos.x, destPos.y);
-	clip(srcRectC, dstRectC);
+	clip(srcRectC, dstRectC, src.w, src.h, flip);
 
 	const byte *srcPtr = (const byte *)src.getBasePtr(srcRectC.left, srcRectC.top);
 	const byte *maskPtr = (const byte *)mask.getBasePtr(srcRectC.left, srcRectC.top);
 	byte *dstPtr = (byte *)getBasePtr(dstRectC.left, dstRectC.top);
 
-	if (format == src.format) {
+	if (format == src.format && !alpha && aMod == 0xff && flip == 0) {
 		maskBlit(dstPtr, srcPtr, maskPtr, pitch, src.pitch, mask.pitch, srcRectC.width(), srcRectC.height(),
 			format.bytesPerPixel);
 	} else if (src.format.isCLUT8()) {
@@ -453,11 +489,21 @@ void ManagedSurface::maskBlitFromInner(const Surface &src, const Surface &mask,
 
 		uint32 map[256];
 		convertPaletteToMap(map, srcPalette->data(), srcPalette->size(), format);
-		crossMaskBlitMap(dstPtr, srcPtr, maskPtr, pitch, src.pitch, mask.pitch, srcRectC.width(), srcRectC.height(),
-			format.bytesPerPixel, map);
+		if (alpha || aMod != 0xff || flip != 0) {
+			alphaMaskBlitMap(dstPtr, srcPtr, maskPtr, pitch, src.pitch, mask.pitch, srcRectC.width(), srcRectC.height(),
+				format, map, flip, aMod);
+		} else {
+			crossMaskBlitMap(dstPtr, srcPtr, maskPtr, pitch, src.pitch, mask.pitch, srcRectC.width(), srcRectC.height(),
+				format.bytesPerPixel, map);
+		}
 	} else {
-		crossMaskBlit(dstPtr, srcPtr, maskPtr, pitch, src.pitch, mask.pitch, srcRectC.width(), srcRectC.height(),
-			format, src.format);
+		if (alpha || aMod != 0xff || flip != 0) {
+			alphaMaskBlit(dstPtr, srcPtr, maskPtr, pitch, src.pitch, mask.pitch, srcRectC.width(), srcRectC.height(),
+				format, src.format, flip, aMod);
+		} else {
+			crossMaskBlit(dstPtr, srcPtr, maskPtr, pitch, src.pitch, mask.pitch, srcRectC.width(), srcRectC.height(),
+				format, src.format);
+		}
 	}
 
 	addDirtyRect(dstRectC);
diff --git a/graphics/managed_surface.h b/graphics/managed_surface.h
index 292e4442218..a741575527c 100644
--- a/graphics/managed_surface.h
+++ b/graphics/managed_surface.h
@@ -90,14 +90,15 @@ protected:
 	 */
 	void simpleBlitFromInner(const Surface &src, const Common::Rect &srcRect,
 		const Common::Point &destPos, const Palette *srcPalette,
-		bool transparentColorSet, uint transparentColor);
+		bool transparentColorSet, uint transparentColor,
+		byte flip, bool alpha, byte aMod);
 
 	/**
 	 * Inner method for blitting with a transparent mask.
 	 */
 	void maskBlitFromInner(const Surface &src, const Surface &mask,
 		const Common::Rect &srcRect, const Common::Point &destPos,
-		const Palette *srcPalette);
+		const Palette *srcPalette, byte flip, bool alpha, byte aMod);
 
 	/**
 	 * Inner method for blitting.
@@ -329,66 +330,84 @@ public:
 	/**
 	 * Copy another surface into this one.
 	 */
-	void simpleBlitFrom(const Surface &src, const Palette *srcPalette = nullptr);
+	void simpleBlitFrom(const Surface &src,
+		byte flip = FLIP_NONE, bool alpha = false, byte aMod = 0xff,
+		const Palette *srcPalette = nullptr);
 
 	/**
 	 * Copy another surface into this one at a given destination position.
 	 */
-	void simpleBlitFrom(const Surface &src, const Common::Point &destPos, const Palette *srcPalette = nullptr);
+	void simpleBlitFrom(const Surface &src, const Common::Point &destPos,
+		byte flip = FLIP_NONE, bool alpha = false, byte aMod = 0xff,
+		const Palette *srcPalette = nullptr);
 
 	/**
 	 * Copy another surface into this one at a given destination position.
 	 */
 	void simpleBlitFrom(const Surface &src, const Common::Rect &srcRect,
-		const Common::Point &destPos, const Palette *srcPalette = nullptr);
+		const Common::Point &destPos,
+		byte flip = FLIP_NONE, bool alpha = false, byte aMod = 0xff,
+		const Palette *srcPalette = nullptr);
 
 	/**
 	 * Copy another surface into this one.
 	 */
-	void simpleBlitFrom(const ManagedSurface &src);
+	void simpleBlitFrom(const ManagedSurface &src,
+		byte flip = FLIP_NONE, bool alpha = false, byte aMod = 0xff);
 
 	/**
 	 * Copy another surface into this one at a given destination position.
 	 */
-	void simpleBlitFrom(const ManagedSurface &src, const Common::Point &destPos);
+	void simpleBlitFrom(const ManagedSurface &src, const Common::Point &destPos,
+		byte flip = FLIP_NONE, bool alpha = false, byte aMod = 0xff);
 
 	/**
 	 * Copy another surface into this one at a given destination position.
 	 */
 	void simpleBlitFrom(const ManagedSurface &src, const Common::Rect &srcRect,
-		const Common::Point &destPos);
+		const Common::Point &destPos,
+		byte flip = FLIP_NONE, bool alpha = false, byte aMod = 0xff);
 
 	/**
 	 * Copy another surface into this one using a transparency mask.
 	 */
-	void maskBlitFrom(const Surface &src, const Surface &mask, const Palette *srcPalette = nullptr);
+	void maskBlitFrom(const Surface &src, const Surface &mask,
+		byte flip = FLIP_NONE, bool alpha = false, byte aMod = 0xff,
+		const Palette *srcPalette = nullptr);
 
 	/**
 	 * Copy another surface into this one at a given destination position using a transparency mask.
 	 */
-	void maskBlitFrom(const Surface &src, const Surface &mask, const Common::Point &destPos, const Palette *srcPalette = nullptr);
+	void maskBlitFrom(const Surface &src, const Surface &mask, const Common::Point &destPos,
+		byte flip = FLIP_NONE, bool alpha = false, byte aMod = 0xff,
+		const Palette *srcPalette = nullptr);
 
 	/**
 	 * Copy another surface into this one at a given destination position using a transparency mask.
 	 */
 	void maskBlitFrom(const Surface &src, const Surface &mask, const Common::Rect &srcRect,
-		const Common::Point &destPos, const Palette *srcPalette = nullptr);
+		const Common::Point &destPos,
+		byte flip = FLIP_NONE, bool alpha = false, byte aMod = 0xff,
+		const Palette *srcPalette = nullptr);
 
 	/**
 	 * Copy another surface into this one using a transparency mask.
 	 */
-	void maskBlitFrom(const ManagedSurface &src, const ManagedSurface &mask);
+	void maskBlitFrom(const ManagedSurface &src, const ManagedSurface &mask,
+		byte flip = FLIP_NONE, bool alpha = false, byte aMod = 0xff);
 
 	/**
 	 * Copy another surface into this one at a given destination position using a transparency mask.
 	 */
-	void maskBlitFrom(const ManagedSurface &src, const ManagedSurface &mask, const Common::Point &destPos);
+	void maskBlitFrom(const ManagedSurface &src, const ManagedSurface &mask, const Common::Point &destPos,
+		byte flip = FLIP_NONE, bool alpha = false, byte aMod = 0xff);
 
 	/**
 	 * Copy another surface into this one at a given destination position using a transparency mask.
 	 */
 	void maskBlitFrom(const ManagedSurface &src, const ManagedSurface &mask,
-		const Common::Rect &srcRect, const Common::Point &destPos);
+		const Common::Rect &srcRect, const Common::Point &destPos,
+		byte flip = FLIP_NONE, bool alpha = false, byte aMod = 0xff);
 
 	/**
 	 * Copy another surface into this one.


Commit: 18e4df9a8fed346020ddccfba725f53bcad3da8d
    https://github.com/scummvm/scummvm/commit/18e4df9a8fed346020ddccfba725f53bcad3da8d
Author: Cameron Cawley (ccawley2011 at gmail.com)
Date: 2025-11-17T14:42:55+02:00

Commit Message:
GRAPHICS: Ensure that ManagedSurface transparent colours are converted

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


diff --git a/graphics/managed_surface.cpp b/graphics/managed_surface.cpp
index f076ca8acb6..467df2ae585 100644
--- a/graphics/managed_surface.cpp
+++ b/graphics/managed_surface.cpp
@@ -245,6 +245,20 @@ void ManagedSurface::copyFrom(const Surface &surf) {
 	}
 }
 
+uint32 ManagedSurface::convertTransparentColor(const ManagedSurface &surf, const PixelFormat &dstFmt) const {
+	if (surf.format == dstFmt) {
+		return surf._transparentColor;
+	} else if (surf.format.isCLUT8()) {
+		byte r, g, b;
+		surf._palette->get(surf._transparentColor, r, g, b);
+		return dstFmt.RGBToColor(r, g, b);
+	} else {
+		byte a, r, g, b;
+		surf.format.colorToARGB(surf._transparentColor, a, r, g, b);
+		return dstFmt.ARGBToColor(a, r, g, b);
+	}
+}
+
 void ManagedSurface::convertFrom(const ManagedSurface &surf, const PixelFormat &fmt) {
 	// Surface::copyFrom frees pixel pointer so let's free up ManagedSurface to be coherent
 	free();
@@ -258,7 +272,7 @@ void ManagedSurface::convertFrom(const ManagedSurface &surf, const PixelFormat &
 
 	// Copy miscellaneous properties
 	_transparentColorSet = surf._transparentColorSet;
-	_transparentColor = surf._transparentColor;
+	_transparentColor = convertTransparentColor(surf, fmt);
 	_palette = (fmt.isCLUT8() && surf._palette) ? new Palette(*surf._palette) : nullptr;
 }
 
@@ -282,6 +296,16 @@ void ManagedSurface::convertFrom(const Surface &surf, const PixelFormat &fmt) {
 	}
 }
 
+void ManagedSurface::convertToInPlace(const PixelFormat &dstFormat) {
+	// Convert miscellaneous properties
+	_transparentColor = convertTransparentColor(*this, dstFormat);
+
+	if (_palette)
+		_innerSurface.convertToInPlace(dstFormat, _palette->data(), _palette->size());
+	else
+		_innerSurface.convertToInPlace(dstFormat);
+}
+
 Graphics::ManagedSurface *ManagedSurface::scale(int16 newWidth, int16 newHeight, bool filtering) const {
 	Graphics::ManagedSurface *target = new Graphics::ManagedSurface();
 
diff --git a/graphics/managed_surface.h b/graphics/managed_surface.h
index a741575527c..96e3c8a839c 100644
--- a/graphics/managed_surface.h
+++ b/graphics/managed_surface.h
@@ -120,6 +120,8 @@ protected:
 		const Common::Rect &destRect, const int flipping, const uint colorMod,
 		const TSpriteBlendMode blend, const AlphaType alphaType);
 
+	uint32 convertTransparentColor(const ManagedSurface &surf, const PixelFormat &dstFmt) const;
+
 public:
 	/**
 	 * Clip the given source bounds so the passed destBounds will be entirely on-screen.
@@ -994,9 +996,7 @@ public:
 	 *
 	 * @param dstFormat  The desired format.
 	 */
-	void convertToInPlace(const PixelFormat &dstFormat) {
-		_innerSurface.convertToInPlace(dstFormat);
-	}
+	void convertToInPlace(const PixelFormat &dstFormat);
 
 	/**
 	 * Convert the data to another pixel format.


Commit: 8ebbcb193a77b1900edb04d067de031d687ee733
    https://github.com/scummvm/scummvm/commit/8ebbcb193a77b1900edb04d067de031d687ee733
Author: Cameron Cawley (ccawley2011 at gmail.com)
Date: 2025-11-17T14:42:55+02:00

Commit Message:
NGI: Replace per-pixel alpha with masks and colour keys

Changed paths:
    engines/ngi/gfx.cpp
    engines/ngi/gfx.h
    engines/ngi/ngi.cpp
    engines/ngi/ngi.h


diff --git a/engines/ngi/gfx.cpp b/engines/ngi/gfx.cpp
index 1fd6d50c5fa..d5079116ab7 100644
--- a/engines/ngi/gfx.cpp
+++ b/engines/ngi/gfx.cpp
@@ -703,6 +703,7 @@ Bitmap::Bitmap() {
 	_flags = 0;
 	_flipping = Graphics::FLIP_NONE;
 	_surface = nullptr;
+	_mask = nullptr;
 }
 
 Bitmap::Bitmap(const Bitmap &src) {
@@ -714,14 +715,27 @@ Bitmap::Bitmap(const Bitmap &src) {
 	_width = src._width;
 	_height = src._height;
 	_flipping = src._flipping;
-	_surface = new Graphics::ManagedSurface();
-	_surface->create(_width, _height, Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0));
-	_surface->copyFrom(*src._surface);
+	_surface = nullptr;
+	_mask = nullptr;
+	if (src._surface) {
+		_surface = new Graphics::ManagedSurface();
+		_surface->copyFrom(*src._surface);
+	}
+	if (src._mask) {
+		_mask = new Graphics::ManagedSurface();
+		_mask->copyFrom(*src._mask);
+	}
 }
 
 Bitmap::~Bitmap() {
-	_surface->free();
-	delete _surface;
+	if (_surface) {
+		_surface->free();
+		delete _surface;
+	}
+	if (_mask) {
+		_mask->free();
+		delete _mask;
+	}
 }
 
 void Bitmap::load(Common::ReadStream *s) {
@@ -747,13 +761,16 @@ bool Bitmap::isPixelHitAtPos(int x, int y) {
 	if (!_surface)
 		return false;
 
-	return ((*((int32 *)_surface->getBasePtr(x - _x, y - _y)) & 0xff) != 0);
+	if (_mask)
+		return (*((uint8 *)_mask->getBasePtr(x - _x, y - _y)) != 0);
+
+	if (_surface->hasTransparentColor())
+		return _surface->getPixel(x - _x, y - _y) != _surface->getTransparentColor();
+
+	return true;
 }
 
 void Bitmap::decode(byte *pixels, const Palette &palette) {
-	_surface = new Graphics::ManagedSurface();
-	_surface->create(_width, _height, Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0));
-
 	if (_type == MKTAG('R', 'B', '\0', '\0'))
 		putDibRB(pixels, palette);
 	else
@@ -783,14 +800,14 @@ void Bitmap::putDib(int x, int y, const Palette &palette, byte alpha) {
 	if (y1 < 0)
 		y1 = 0;
 
-	uint32 alphac = MS_ARGB(alpha, 0xff, 0xff, 0xff);
-
-	g_nmi->_backgroundSurface.blendBlitFrom(*_surface, sub, Common::Point(x1, y1), _flipping, alphac);
+	if (_mask)
+		g_nmi->_backgroundSurface.maskBlitFrom(*_surface, *_mask, sub, Common::Point(x1, y1), _flipping, false, alpha);
+	else
+		g_nmi->_backgroundSurface.simpleBlitFrom(*_surface, sub, Common::Point(x1, y1), _flipping, false, alpha);
 	g_nmi->_system->copyRectToScreen(g_nmi->_backgroundSurface.getBasePtr(x1, y1), g_nmi->_backgroundSurface.pitch, x1, y1, sub.width(), sub.height());
 }
 
-bool Bitmap::putDibRB(byte *pixels, const Palette &palette) {
-	uint32 *curDestPtr;
+void Bitmap::putDibRB(byte *pixels, const Palette &palette) {
 	int endy;
 	int x;
 	int start1;
@@ -802,9 +819,16 @@ bool Bitmap::putDibRB(byte *pixels, const Palette &palette) {
 
 	if (!palette.size) {
 		debugC(2, kDebugDrawing, "Bitmap::putDibRB(): Both global and local palettes are empty");
-		return false;
+		return;
 	}
 
+	_surface = new Graphics::ManagedSurface();
+	_surface->create(_width, _height, Graphics::PixelFormat::createFormatCLUT8());
+	_mask = new Graphics::ManagedSurface();
+	_mask->create(_width, _height, Graphics::PixelFormat::createFormatCLUT8());
+
+	convertPalette(palette);
+
 	debugC(8, kDebugDrawing, "Bitmap::putDibRB()");
 
 	endy = _height - 1;
@@ -854,10 +878,10 @@ bool Bitmap::putDibRB(byte *pixels, const Palette &palette) {
 				if (fillLen > 0 || start1 >= 0) {
 					if (x <= _width + 1 || (fillLen += _width - x + 1, fillLen > 0)) {
 						if (y <= endy) {
-							int bgcolor = palette.pal[(pixel >> 8) & 0xff];
-							curDestPtr = (uint32 *)_surface->getBasePtr(start1, y);
+							uint8 *curDestPtr = (uint8 *)_surface->getBasePtr(start1, y);
+							uint8 *curMaskPtr = (uint8 *)_mask->getBasePtr(start1, y);
 							fillLen = MIN(_width - start1, fillLen);
-							colorFill(curDestPtr, fillLen, bgcolor);
+							colorFill(curDestPtr, curMaskPtr, (pixel >> 8) & 0xff, fillLen);
 						}
 					}
 				}
@@ -882,19 +906,17 @@ bool Bitmap::putDibRB(byte *pixels, const Palette &palette) {
 				}
 
 				if (y <= endy) {
-					curDestPtr = (uint32 *)_surface->getBasePtr(start1, y);
+					uint8 *curDestPtr = (uint8 *)_surface->getBasePtr(start1, y);
+					uint8 *curMaskPtr = (uint8 *)_mask->getBasePtr(start1, y);
 					fillLen = MIN(_width - start1, fillLen);
-					paletteFill(curDestPtr, (byte *)srcPtr2, fillLen, palette);
+					paletteFill(curDestPtr, curMaskPtr, (byte *)srcPtr2, fillLen);
 				}
 			}
 		}
 	}
-
-	return false;
 }
 
 void Bitmap::putDibCB(byte *pixels, const Palette &palette) {
-	uint32 *curDestPtr;
 	int endx;
 	int endy;
 	int bpp;
@@ -920,20 +942,47 @@ void Bitmap::putDibCB(byte *pixels, const Palette &palette) {
 	int starty = 0;
 	int startx = 0;
 
-	if (_flags & 0x1000000) {
+	if (cb05_format) {
+		_surface = new Graphics::ManagedSurface();
+		_surface->create(_width, _height, Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0));
+
+		if (_flags & 0x1000000)
+			_surface->setTransparentColor(0);
+
 		for (int y = starty; y <= endy; srcPtr -= pitch, y++) {
-			curDestPtr = (uint32 *)_surface->getBasePtr(startx, y);
-			copierKeyColor(curDestPtr, srcPtr, endx - startx + 1, _flags & 0xff, palette, cb05_format);
+			uint16 *curDestPtr = (uint16 *)_surface->getBasePtr(startx, y);
+			copier16(curDestPtr, srcPtr, endx - startx + 1);
 		}
+
+		// Converting from true colour is slow compared to converting
+		// from paletted surfaces, so we do this up-front.
+		_surface->convertToInPlace(g_system->getScreenFormat());
 	} else {
+		_surface = new Graphics::ManagedSurface();
+		_surface->create(_width, _height, Graphics::PixelFormat::createFormatCLUT8());
+
+		if (_flags & 0x1000000)
+			_surface->setTransparentColor(_flags & 0xff);
+		convertPalette(palette);
+
 		for (int y = starty; y <= endy; srcPtr -= pitch, y++) {
-			curDestPtr = (uint32 *)_surface->getBasePtr(startx, y);
-			copier(curDestPtr, srcPtr, endx - startx + 1, palette, cb05_format);
+			uint8 *curDestPtr = (uint8 *)_surface->getBasePtr(startx, y);
+			copier8(curDestPtr, srcPtr, endx - startx + 1);
 		}
 	}
 }
 
-void Bitmap::colorFill(uint32 *dest, int len, int32 color) {
+void Bitmap::convertPalette(const Palette &palette) {
+	constexpr Graphics::PixelFormat format(2, 5, 6, 5, 0, 11, 5, 0, 0);
+
+	for (uint i = 0; i < palette.size; i++) {
+		byte col[3];
+		format.colorToRGB(palette.pal[i] & 0xffff, col[0], col[1], col[2]);
+		_surface->setPalette(col, i, 1);
+	}
+}
+
+void Bitmap::colorFill(uint8 *dest, uint8 *mask, byte color, int len) {
 #if 0
 	if (blendMode) {
 		if (blendMode != 1)
@@ -944,17 +993,13 @@ void Bitmap::colorFill(uint32 *dest, int len, int32 color) {
 		colorFill = ptrfillColor16bit;
 	}
 #endif
-	byte r, g, b;
-
-	g_nmi->_origFormat.colorToRGB(color, r, g, b);
-
-	uint32 c = MS_ARGB(0xff, r, g, b);
-
-	for (int i = 0; i < len; i++)
-		*dest++ = c;
+	for (int i = 0; i < len; i++) {
+		*dest++ = color;
+		*mask++ = 0xff;
+	}
 }
 
-void Bitmap::paletteFill(uint32 *dest, byte *src, int len, const Palette &palette) {
+void Bitmap::paletteFill(uint8 *dest, uint8 *mask, byte *src, int len) {
 #if 0
 	if (blendMode) {
 		if (blendMode != 1)
@@ -966,93 +1011,47 @@ void Bitmap::paletteFill(uint32 *dest, byte *src, int len, const Palette &palett
 	}
 #endif
 
-	byte r, g, b;
-
 	for (int i = 0; i < len; i++) {
-		g_nmi->_origFormat.colorToRGB(palette.pal[*src++] & 0xffff, r, g, b);
-
-		*dest++ = MS_ARGB(0xff, r, g, b);
+		*dest++ = *src++;
+		*mask++ = 0xff;
 	}
 }
 
-void Bitmap::copierKeyColor(uint32 *dest, byte *src, int len, int keyColor, const Palette &palette, bool cb05_format) {
+void Bitmap::copier8(uint8 *dest, byte *src, int len) {
 #if 0
 	if (blendMode) {
 		if (blendMode == 1) {
-			if (cb05_format)
-				copierKeyColor = ptrcopier16bitKeycolorAlpha;
-			else
-				copierKeyColor = ptrcopierKeycolorAlpha;
+			copier = ptrcopierWithPaletteAlpha;
 		} else {
 			copier = 0;
 		}
-	} else if (cb05_format) {
-		copierKeyColor = ptrcopier16bitKeycolor;
 	} else {
-		copierKeyColor = ptrkeyColor16bit;
+		copier = ptrcopierWithPalette;
 	}
 #endif
 
-	byte r, g, b;
-
-	if (!cb05_format) {
-		for (int i = 0; i < len; i++) {
-			if (*src != keyColor) {
-				g_nmi->_origFormat.colorToRGB(palette.pal[*src] & 0xffff, r, g, b);
-				*dest = MS_ARGB(0xff, r, g, b);
-			}
-
-			dest++;
-			src++;
-		}
-	} else {
-		int16 *src16 = (int16 *)src;
-
-		for (int i = 0; i < len; i++) {
-			if (*src16 != 0) {
-				g_nmi->_origFormat.colorToRGB(READ_LE_UINT16(src16), r, g, b);
-				*dest = MS_ARGB(0xff, r, g, b);
-			}
-
-			dest++;
-			src16++;
-		}
+	for (int i = 0; i < len; i++) {
+		*dest++ = *src++;
 	}
 }
 
-void Bitmap::copier(uint32 *dest, byte *src, int len, const Palette &palette, bool cb05_format) {
+void Bitmap::copier16(uint16 *dest, byte *src, int len) {
 #if 0
 	if (blendMode) {
 		if (blendMode == 1) {
-			if (cb05_format)
-				copier = ptrcopier16bitAlpha;
-			else
-				copier = ptrcopierWithPaletteAlpha;
+			copier = ptrcopier16bitAlpha;
 		} else {
 			copier = 0;
 		}
-	} else if (cb05_format) {
-		copier = ptrcopier16bit;
 	} else {
-		copier = ptrcopierWithPalette;
+		copier = ptrcopier16bit;
 	}
 #endif
 
-	byte r, g, b;
-
-	if (!cb05_format) {
-		for (int i = 0; i < len; i++) {
-			g_nmi->_origFormat.colorToRGB(palette.pal[*src++] & 0xffff, r, g, b);
+	int16 *src16 = (int16 *)src;
 
-			*dest++ = MS_ARGB(0xff, r, g, b);
-		}
-	} else {
-		int16 *src16 = (int16 *)src;
-
-		for (int i = 0; i < len; i++) {
-			g_nmi->_origFormat.colorToRGB(READ_LE_UINT16(src16++), r, g, b);
-			*dest++ = MS_ARGB(0xff, r, g, b);
-		}
+	for (int i = 0; i < len; i++) {
+		*dest++ = READ_LE_UINT16(src16++);
 	}
 }
 
diff --git a/engines/ngi/gfx.h b/engines/ngi/gfx.h
index 522b2b08e24..cd8723e0b6e 100644
--- a/engines/ngi/gfx.h
+++ b/engines/ngi/gfx.h
@@ -47,6 +47,7 @@ struct Bitmap {
 	int _flags;
 	int _flipping;
 	Graphics::ManagedSurface *_surface;
+	Graphics::ManagedSurface *_mask;
 
 	Bitmap();
 	Bitmap(const Bitmap &src);
@@ -55,13 +56,15 @@ struct Bitmap {
 	void load(Common::ReadStream *s);
 	void decode(byte *pixels, const Palette &palette);
 	void putDib(int x, int y, const Palette &palette, byte alpha);
-	bool putDibRB(byte *pixels, const Palette &palette);
+	void putDibRB(byte *pixels, const Palette &palette);
 	void putDibCB(byte *pixels, const Palette &palette);
 
-	void colorFill(uint32 *dest, int len, int32 color);
-	void paletteFill(uint32 *dest, byte *src, int len, const Palette &palette);
-	void copierKeyColor(uint32 *dest, byte *src, int len, int keyColor, const Palette &palette, bool cb05_format);
-	void copier(uint32 *dest, byte *src, int len, const Palette &palette, bool cb05_format);
+	void convertPalette(const Palette &palette);
+
+	void colorFill(uint8 *dest, uint8 *mask, byte color, int len);
+	void paletteFill(uint8 *dest, uint8 *mask, byte *src, int len);
+	void copier8(uint8 *dest, byte *src, int len);
+	void copier16(uint16 *dest, byte *src, int len);
 
 	/** ownership of returned object is transferred to caller */
 	Bitmap *reverseImage(bool flip = true) const;
diff --git a/engines/ngi/ngi.cpp b/engines/ngi/ngi.cpp
index 3c9d89a991c..02034c82df7 100644
--- a/engines/ngi/ngi.cpp
+++ b/engines/ngi/ngi.cpp
@@ -245,7 +245,6 @@ Common::Error NGIEngine::run() {
 	initGraphics(800, 600, &format);
 
 	_backgroundSurface.create(800, 600, format);
-	_origFormat = Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0);
 
 	_globalMessageQueueList.reset(new GlobalMessageQueueList);
 	_behaviorManager.reset(new BehaviorManager);
diff --git a/engines/ngi/ngi.h b/engines/ngi/ngi.h
index 2092835887c..6522fb1aa04 100644
--- a/engines/ngi/ngi.h
+++ b/engines/ngi/ngi.h
@@ -155,7 +155,6 @@ public:
 	void updateEvents();
 
 	Graphics::ManagedSurface _backgroundSurface;
-	Graphics::PixelFormat _origFormat;
 
 	Common::ScopedPtr<GameLoader> _gameLoader;
 	GameProject *_gameProject;


Commit: 732a19c1eaece169ba0ddab963fcf9a949675f73
    https://github.com/scummvm/scummvm/commit/732a19c1eaece169ba0ddab963fcf9a949675f73
Author: Cameron Cawley (ccawley2011 at gmail.com)
Date: 2025-11-17T14:42:55+02:00

Commit Message:
NGI: Support arbitrary pixel formats

Changed paths:
    engines/ngi/gfx.cpp
    engines/ngi/ngi.cpp


diff --git a/engines/ngi/gfx.cpp b/engines/ngi/gfx.cpp
index d5079116ab7..f7ba1a66fea 100644
--- a/engines/ngi/gfx.cpp
+++ b/engines/ngi/gfx.cpp
@@ -1180,17 +1180,21 @@ DynamicPhase *Shadows::findSize(int width, int height) {
 }
 
 void NGIEngine::drawAlphaRectangle(int x1, int y1, int x2, int y2, int alpha) {
-	for (int y = y1; y < y2; y++) {
-		uint32 *ptr = (uint32 *)g_nmi->_backgroundSurface.getBasePtr(x1, y);
+	// TODO: Let the backend handle this?
+	const Graphics::PixelFormat &format = g_nmi->_backgroundSurface.format;
 
+	for (int y = y1; y < y2; y++) {
 		for (int x = x1; x < x2; x++) {
-			uint32 color = *ptr;
-			color = (((color >> 24) & 0xff) * alpha / 0xff) << 24 |
-					(((color >> 16) & 0xff) * alpha / 0xff) << 16 |
-					(((color >>  8) & 0xff) * alpha / 0xff) <<  8 |
-					(color & 0xff);
-			*ptr = color;
-			ptr++;
+			uint32 color = g_nmi->_backgroundSurface.getPixel(x, y);
+
+			uint8 a, r, g, b;
+			format.colorToARGB(color, a, r, g, b);
+			r = (r * alpha) / 0xff;
+			g = (g * alpha) / 0xff;
+			b = (b * alpha) / 0xff;
+			color = format.ARGBToColor(a, r, g, b);
+
+			g_nmi->_backgroundSurface.setPixel(x, y, color);
 		}
 	}
 }
diff --git a/engines/ngi/ngi.cpp b/engines/ngi/ngi.cpp
index 02034c82df7..adb6d5adef7 100644
--- a/engines/ngi/ngi.cpp
+++ b/engines/ngi/ngi.cpp
@@ -240,9 +240,12 @@ Common::String NGIEngine::getSaveStateName(int slot) const {
 }
 
 Common::Error NGIEngine::run() {
-	const Graphics::PixelFormat format(4, 8, 8, 8, 8, 24, 16, 8, 0);
 	// Initialize backend
-	initGraphics(800, 600, &format);
+	initGraphics(800, 600, nullptr);
+
+	const Graphics::PixelFormat format = g_system->getScreenFormat();
+	if (format.isCLUT8())
+		return Common::kUnsupportedColorMode;
 
 	_backgroundSurface.create(800, 600, format);
 




More information about the Scummvm-git-logs mailing list