[Scummvm-cvs-logs] scummvm master -> 7edc5cd891d903e8ae114238745a965c319d7fa3

wjp wjp at usecode.org
Fri Jul 1 00:54:15 CEST 2016


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

Summary:
c6c9593840 Revert "SCI32: Fix broken Remap implementation"
5a3d8337a6 SCI32: Change kRemapColors naming for consistency
ed64d5b020 SCI32: Move GfxRemap32 to separate files
14e57ac867 SCI32: Add missing remap CelObj calls
0aed08681e SCI32: Separate remap types
0c799e1bd9 SCI32: Move matchColor to GfxRemap32
ca279390a3 SCI32: Fix broken Remap implementation
7edc5cd891 Merge branch 'remap'


Commit: c6c95938405b09204898c7a993ebdfe6ceafcb0e
    https://github.com/scummvm/scummvm/commit/c6c95938405b09204898c7a993ebdfe6ceafcb0e
Author: Willem Jan Palenstijn (wjp at usecode.org)
Date: 2016-07-01T00:18:07+02:00

Commit Message:
Revert "SCI32: Fix broken Remap implementation"

This reverts commit cfda8b9ecd8a6e3c003abe533ea2e2981d8ba984.

This is only to re-apply it immediately in a series of smaller commits.

Changed paths:
  R engines/sci/graphics/remap32.cpp
  R engines/sci/graphics/remap32.h
    engines/sci/engine/kernel.h
    engines/sci/engine/kernel_tables.h
    engines/sci/engine/kgraphics.cpp
    engines/sci/engine/kgraphics32.cpp
    engines/sci/engine/savegame.cpp
    engines/sci/engine/savegame.h
    engines/sci/graphics/celobj32.cpp
    engines/sci/graphics/frameout.cpp
    engines/sci/graphics/palette32.cpp
    engines/sci/graphics/palette32.h
    engines/sci/graphics/plane32.cpp
    engines/sci/graphics/remap.cpp
    engines/sci/graphics/remap.h
    engines/sci/graphics/view.h
    engines/sci/module.mk
    engines/sci/sci.cpp



diff --git a/engines/sci/engine/kernel.h b/engines/sci/engine/kernel.h
index 1202982..b269e97 100644
--- a/engines/sci/engine/kernel.h
+++ b/engines/sci/engine/kernel.h
@@ -412,7 +412,7 @@ reg_t kPlatform(EngineState *s, int argc, reg_t *argv);
 reg_t kTextColors(EngineState *s, int argc, reg_t *argv);
 reg_t kTextFonts(EngineState *s, int argc, reg_t *argv);
 reg_t kShow(EngineState *s, int argc, reg_t *argv);
-reg_t kRemapColors(EngineState *s, int argc, reg_t *argv);
+reg_t kRemapColors16(EngineState *s, int argc, reg_t *argv);
 reg_t kDummy(EngineState *s, int argc, reg_t *argv);
 reg_t kEmpty(EngineState *s, int argc, reg_t *argv);
 reg_t kStub(EngineState *s, int argc, reg_t *argv);
@@ -482,13 +482,13 @@ reg_t kScrollWindowDestroy(EngineState *s, int argc, reg_t *argv);
 
 reg_t kMulDiv(EngineState *s, int argc, reg_t *argv);
 
-reg_t kRemapColors32(EngineState *s, int argc, reg_t *argv);
-reg_t kRemapColorsOff(EngineState *s, int argc, reg_t *argv);
-reg_t kRemapColorsByRange(EngineState *s, int argc, reg_t *argv);
-reg_t kRemapColorsByPercent(EngineState *s, int argc, reg_t *argv);
-reg_t kRemapColorsToGray(EngineState *s, int argc, reg_t *argv);
-reg_t kRemapColorsToPercentGray(EngineState *s, int argc, reg_t *argv);
-reg_t kRemapColorsBlockRange(EngineState *s, int argc, reg_t *argv);
+reg_t kRemapColors(EngineState *s, int argc, reg_t *argv);
+reg_t kRemapOff(EngineState *s, int argc, reg_t *argv);
+reg_t kRemapByRange(EngineState *s, int argc, reg_t *argv);
+reg_t kRemapByPercent(EngineState *s, int argc, reg_t *argv);
+reg_t kRemapToGray(EngineState *s, int argc, reg_t *argv);
+reg_t kRemapToPercentGray(EngineState *s, int argc, reg_t *argv);
+reg_t kRemapSetNoMatchRange(EngineState *s, int argc, reg_t *argv);
 
 reg_t kAddScreenItem(EngineState *s, int argc, reg_t *argv);
 reg_t kUpdateScreenItem(EngineState *s, int argc, reg_t *argv);
diff --git a/engines/sci/engine/kernel_tables.h b/engines/sci/engine/kernel_tables.h
index dacaafe..19bee35 100644
--- a/engines/sci/engine/kernel_tables.h
+++ b/engines/sci/engine/kernel_tables.h
@@ -414,12 +414,12 @@ static const SciKernelMapSubEntry kList_subops[] = {
 
 //    version,         subId, function-mapping,                    signature,              workarounds
 static const SciKernelMapSubEntry kRemapColors_subops[] = {
-	{ SIG_SCI32,           0, MAP_CALL(RemapColorsOff),            "(i)",                  NULL },
-	{ SIG_SCI32,           1, MAP_CALL(RemapColorsByRange),        "iiii(i)",              NULL },
-	{ SIG_SCI32,           2, MAP_CALL(RemapColorsByPercent),      "ii(i)",                NULL },
-	{ SIG_SCI32,           3, MAP_CALL(RemapColorsToGray),         "ii(i)",                NULL },
-	{ SIG_SCI32,           4, MAP_CALL(RemapColorsToPercentGray),  "iii(i)",               NULL },
-	{ SIG_SCI32,           5, MAP_CALL(RemapColorsBlockRange),     "ii",                   NULL },
+	{ SIG_SCI32,           0, MAP_CALL(RemapOff),                 "(i)",                  NULL },
+	{ SIG_SCI32,           1, MAP_CALL(RemapByRange),             "iiii(i)",              NULL },
+	{ SIG_SCI32,           2, MAP_CALL(RemapByPercent),           "ii(i)",                NULL },
+	{ SIG_SCI32,           3, MAP_CALL(RemapToGray),              "ii(i)",                NULL },
+	{ SIG_SCI32,           4, MAP_CALL(RemapToPercentGray),       "iii(i)",               NULL },
+	{ SIG_SCI32,           5, MAP_CALL(RemapSetNoMatchRange),     "ii",                   NULL },
 	SCI_SUBOPENTRY_TERMINATOR
 };
 
@@ -634,9 +634,9 @@ static SciKernelMapEntry s_kernelMap[] = {
 	{ MAP_CALL(PriCoord),          SIG_EVERYWHERE,           "i",                     NULL,            NULL },
 	{ MAP_CALL(Random),            SIG_EVERYWHERE,           "i(i)(i)",               NULL,            NULL },
 	{ MAP_CALL(ReadNumber),        SIG_EVERYWHERE,           "r",                     NULL,            kReadNumber_workarounds },
-	{ MAP_CALL(RemapColors),       SIG_SCI11, SIGFOR_ALL,    "i(i)(i)(i)(i)",         NULL,            NULL },
+	{ "RemapColors", kRemapColors16,       SIG_SCI11, SIGFOR_ALL,    "i(i)(i)(i)(i)", NULL,            NULL },
 #ifdef ENABLE_SCI32
-	{ "RemapColors", kRemapColors32, SIG_SCI32, SIGFOR_ALL,  "i(i)(i)(i)(i)(i)",      kRemapColors_subops, NULL },
+	{ MAP_CALL(RemapColors),       SIG_SCI32, SIGFOR_ALL,    "i(i)(i)(i)(i)(i)",      kRemapColors_subops, NULL },
 #endif
 	{ MAP_CALL(ResCheck),          SIG_EVERYWHERE,           "ii(iiii)",              NULL,            NULL },
 	{ MAP_CALL(RespondsTo),        SIG_EVERYWHERE,           ".i",                    NULL,            NULL },
diff --git a/engines/sci/engine/kgraphics.cpp b/engines/sci/engine/kgraphics.cpp
index cae5a09..73236b9 100644
--- a/engines/sci/engine/kgraphics.cpp
+++ b/engines/sci/engine/kgraphics.cpp
@@ -1258,7 +1258,7 @@ reg_t kShow(EngineState *s, int argc, reg_t *argv) {
 }
 
 // Early variant of the SCI32 kRemapColors kernel function, used in the demo of QFG4
-reg_t kRemapColors(EngineState *s, int argc, reg_t *argv) {
+reg_t kRemapColors16(EngineState *s, int argc, reg_t *argv) {
 	uint16 operation = argv[0].toUint16();
 
 	switch (operation) {
diff --git a/engines/sci/engine/kgraphics32.cpp b/engines/sci/engine/kgraphics32.cpp
index 019a069..9270c81 100644
--- a/engines/sci/engine/kgraphics32.cpp
+++ b/engines/sci/engine/kgraphics32.cpp
@@ -54,7 +54,6 @@
 #include "sci/graphics/frameout.h"
 #include "sci/graphics/paint32.h"
 #include "sci/graphics/palette32.h"
-#include "sci/graphics/remap32.h"
 #include "sci/graphics/text32.h"
 #endif
 
@@ -926,69 +925,57 @@ reg_t kPalCycle(EngineState *s, int argc, reg_t *argv) {
 	return s->r_acc;
 }
 
-reg_t kRemapColors32(EngineState *s, int argc, reg_t *argv) {
+reg_t kRemapColors(EngineState *s, int argc, reg_t *argv) {
 	if (!s)
 		return make_reg(0, getSciVersion());
 	error("not supposed to call this");
 }
 
-reg_t kRemapColorsOff(EngineState *s, int argc, reg_t *argv) {
-	if (argc == 0) {
-		g_sci->_gfxRemap32->remapAllOff();
-	} else {
-		const uint8 color = argv[0].toUint16();
-		g_sci->_gfxRemap32->remapOff(color);
-	}
+reg_t kRemapOff(EngineState *s, int argc, reg_t *argv) {
+	byte color = (argc >= 1) ? argv[0].toUint16() : 0;
+	g_sci->_gfxRemap32->remapOff(color);
 	return s->r_acc;
 }
 
-reg_t kRemapColorsByRange(EngineState *s, int argc, reg_t *argv) {
-	const uint8 color = argv[0].toUint16();
-	const int16 from = argv[1].toSint16();
-	const int16 to = argv[2].toSint16();
-	const int16 base = argv[3].toSint16();
-	// NOTE: There is an optional last parameter after `base`
-	// which was only used by the priority map debugger, which
-	// does not exist in release versions of SSCI
-	g_sci->_gfxRemap32->remapByRange(color, from, to, base);
+reg_t kRemapByRange(EngineState *s, int argc, reg_t *argv) {
+	byte color = argv[0].toUint16();
+	byte from = argv[1].toUint16();
+	byte to = argv[2].toUint16();
+	byte base = argv[3].toUint16();
+	// The last parameter, depth, is unused
+	g_sci->_gfxRemap32->setRemappingRange(color, from, to, base);
 	return s->r_acc;
 }
 
-reg_t kRemapColorsByPercent(EngineState *s, int argc, reg_t *argv) {
-	const uint8 color = argv[0].toUint16();
-	const int16 percent = argv[1].toSint16();
-	// NOTE: There is an optional last parameter after `percent`
-	// which was only used by the priority map debugger, which
-	// does not exist in release versions of SSCI
-	g_sci->_gfxRemap32->remapByPercent(color, percent);
+reg_t kRemapByPercent(EngineState *s, int argc, reg_t *argv) {
+	byte color = argv[0].toUint16();
+	byte percent = argv[1].toUint16();
+	// The last parameter, depth, is unused
+	g_sci->_gfxRemap32->setRemappingPercent(color, percent);
 	return s->r_acc;
 }
 
-reg_t kRemapColorsToGray(EngineState *s, int argc, reg_t *argv) {
-	const uint8 color = argv[0].toUint16();
-	const int16 gray = argv[1].toSint16();
-	// NOTE: There is an optional last parameter after `gray`
-	// which was only used by the priority map debugger, which
-	// does not exist in release versions of SSCI
-	g_sci->_gfxRemap32->remapToGray(color, gray);
+reg_t kRemapToGray(EngineState *s, int argc, reg_t *argv) {
+	byte color = argv[0].toUint16();
+	byte gray = argv[1].toUint16();
+	// The last parameter, depth, is unused
+	g_sci->_gfxRemap32->setRemappingToGray(color, gray);
 	return s->r_acc;
 }
 
-reg_t kRemapColorsToPercentGray(EngineState *s, int argc, reg_t *argv) {
-	const uint8 color = argv[0].toUint16();
-	const int16 gray = argv[1].toSint16();
-	const int16 percent = argv[2].toSint16();
-	// NOTE: There is an optional last parameter after `percent`
-	// which was only used by the priority map debugger, which
-	// does not exist in release versions of SSCI
-	g_sci->_gfxRemap32->remapToPercentGray(color, gray, percent);
+reg_t kRemapToPercentGray(EngineState *s, int argc, reg_t *argv) {
+	byte color = argv[0].toUint16();
+	byte gray = argv[1].toUint16();
+	byte percent = argv[2].toUint16();
+	// The last parameter, depth, is unused
+	g_sci->_gfxRemap32->setRemappingToPercentGray(color, gray, percent);
 	return s->r_acc;
 }
 
-reg_t kRemapColorsBlockRange(EngineState *s, int argc, reg_t *argv) {
-	const uint8 from = argv[0].toUint16();
-	const uint8 count = argv[1].toUint16();
-	g_sci->_gfxRemap32->blockRange(from, count);
+reg_t kRemapSetNoMatchRange(EngineState *s, int argc, reg_t *argv) {
+	byte from = argv[0].toUint16();
+	byte count = argv[1].toUint16();
+	g_sci->_gfxRemap32->setNoMatchRange(from, count);
 	return s->r_acc;
 }
 
diff --git a/engines/sci/engine/savegame.cpp b/engines/sci/engine/savegame.cpp
index 0972aec..302f046 100644
--- a/engines/sci/engine/savegame.cpp
+++ b/engines/sci/engine/savegame.cpp
@@ -48,9 +48,8 @@
 #include "sci/sound/music.h"
 
 #ifdef ENABLE_SCI32
-#include "sci/graphics/frameout.h"
 #include "sci/graphics/palette32.h"
-#include "sci/graphics/remap32.h"
+#include "sci/graphics/frameout.h"
 #endif
 
 namespace Sci {
@@ -808,33 +807,6 @@ void GfxPalette32::saveLoadWithSerializer(Common::Serializer &s) {
 		}
 	}
 }
-
-void GfxRemap32::saveLoadWithSerializer(Common::Serializer &s) {
-	if (s.getVersion() < 35) {
-		return;
-	}
-
-	s.syncAsByte(_numActiveRemaps);
-	s.syncAsByte(_blockedRangeStart);
-	s.syncAsSint16LE(_blockedRangeCount);
-
-	for (uint i = 0; i < _remaps.size(); ++i) {
-		SingleRemap &singleRemap = _remaps[i];
-		s.syncAsByte(singleRemap._type);
-		if (s.isLoading() && singleRemap._type != kRemapNone) {
-			singleRemap.reset();
-		}
-		s.syncAsByte(singleRemap._from);
-		s.syncAsByte(singleRemap._to);
-		s.syncAsByte(singleRemap._delta);
-		s.syncAsByte(singleRemap._percent);
-		s.syncAsByte(singleRemap._gray);
-	}
-
-	if (s.isLoading()) {
-		_needsUpdate = true;
-	}
-}
 #endif
 
 void GfxPorts::saveLoadWithSerializer(Common::Serializer &s) {
diff --git a/engines/sci/engine/savegame.h b/engines/sci/engine/savegame.h
index 43909ac..459e992 100644
--- a/engines/sci/engine/savegame.h
+++ b/engines/sci/engine/savegame.h
@@ -37,7 +37,6 @@ struct EngineState;
  *
  * Version - new/changed feature
  * =============================
- *      35 - SCI32 remap
  *      34 - SCI32 palettes, and store play time in ticks
  *      33 - new overridePriority flag in MusicEntry
  *      32 - new playBed flag in MusicEntry
@@ -60,7 +59,7 @@ struct EngineState;
  */
 
 enum {
-	CURRENT_SAVEGAME_VERSION = 35,
+	CURRENT_SAVEGAME_VERSION = 34,
 	MINIMUM_SAVEGAME_VERSION = 14
 };
 
diff --git a/engines/sci/graphics/celobj32.cpp b/engines/sci/graphics/celobj32.cpp
index befa5cd..3018c6b 100644
--- a/engines/sci/graphics/celobj32.cpp
+++ b/engines/sci/graphics/celobj32.cpp
@@ -26,12 +26,13 @@
 #include "sci/graphics/celobj32.h"
 #include "sci/graphics/frameout.h"
 #include "sci/graphics/palette32.h"
-#include "sci/graphics/remap32.h"
+#include "sci/graphics/picture.h"
+#include "sci/graphics/remap.h"
 #include "sci/graphics/text32.h"
+#include "sci/graphics/view.h"
 
 namespace Sci {
 #pragma mark CelScaler
-
 CelScaler *CelObj::_scaler = nullptr;
 
 void CelScaler::activateScaleTables(const Ratio &scaleX, const Ratio &scaleY) {
@@ -322,10 +323,6 @@ public:
 #pragma mark -
 #pragma mark CelObj - Remappers
 
-/**
- * Pixel mapper for a CelObj with transparent pixels and no
- * remapping data.
- */
 struct MAPPER_NoMD {
 	inline void draw(byte *target, const byte pixel, const uint8 skipColor) const {
 		if (pixel != skipColor) {
@@ -333,49 +330,25 @@ struct MAPPER_NoMD {
 		}
 	}
 };
-
-/**
- * Pixel mapper for a CelObj with no transparent pixels and
- * no remapping data.
- */
 struct MAPPER_NoMDNoSkip {
 	inline void draw(byte *target, const byte pixel, const uint8) const {
 		*target = pixel;
 	}
 };
 
-/**
- * Pixel mapper for a CelObj with transparent pixels,
- * remapping data, and remapping enabled.
- */
 struct MAPPER_Map {
 	inline void draw(byte *target, const byte pixel, const uint8 skipColor) const {
 		if (pixel != skipColor) {
-			// NOTE: For some reason, SSCI never checks if the source
-			// pixel is *above* the range of remaps.
 			if (pixel < g_sci->_gfxRemap32->getStartColor()) {
 				*target = pixel;
-			} else if (g_sci->_gfxRemap32->remapEnabled(pixel)) {
-				*target = g_sci->_gfxRemap32->remapColor(pixel, *target);
+			} else {
+				if (g_sci->_gfxRemap32->remapEnabled(pixel))
+					*target = g_sci->_gfxRemap32->remapColor(pixel, *target);
 			}
 		}
 	}
 };
 
-/**
- * Pixel mapper for a CelObj with transparent pixels,
- * remapping data, and remapping disabled.
- */
-struct MAPPER_NoMap {
-	inline void draw(byte *target, const byte pixel, const uint8 skipColor) const {
-		// NOTE: For some reason, SSCI never checks if the source
-		// pixel is *above* the range of remaps.
-		if (pixel != skipColor && pixel < g_sci->_gfxRemap32->getStartColor()) {
-			*target = pixel;
-		}
-	}
-};
-
 void CelObj::draw(Buffer &target, const ScreenItem &screenItem, const Common::Rect &targetRect) const {
 	const Common::Point &scaledPosition = screenItem._scaledPosition;
 	const Ratio &scaleX = screenItem._ratioX;
@@ -550,7 +523,6 @@ void CelObj::submitPalette() const {
 
 #pragma mark -
 #pragma mark CelObj - Caching
-
 int CelObj::_nextCacheId = 1;
 CelCache *CelObj::_cache = nullptr;
 
@@ -652,35 +624,33 @@ void dummyFill(Buffer &target, const Common::Rect &targetRect) {
 }
 
 void CelObj::drawHzFlip(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const {
-	render<MAPPER_NoMap, SCALER_NoScale<true, READER_Compressed> >(target, targetRect, scaledPosition);
+	debug("drawHzFlip");
+	dummyFill(target, targetRect);
 }
 
 void CelObj::drawNoFlip(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const {
-	render<MAPPER_NoMap, SCALER_NoScale<false, READER_Compressed> >(target, targetRect, scaledPosition);
+	debug("drawNoFlip");
+	dummyFill(target, targetRect);
 }
 
 void CelObj::drawUncompNoFlip(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const {
-	render<MAPPER_NoMap, SCALER_NoScale<false, READER_Uncompressed> >(target, targetRect, scaledPosition);
+	debug("drawUncompNoFlip");
+	dummyFill(target, targetRect);
 }
 
 void CelObj::drawUncompHzFlip(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const {
-	render<MAPPER_NoMap, SCALER_NoScale<true, READER_Uncompressed> >(target, targetRect, scaledPosition);
+	debug("drawUncompHzFlip");
+	dummyFill(target, targetRect);
 }
 
 void CelObj::scaleDraw(Buffer &target, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition) const {
-	if (_drawMirrored) {
-		render<MAPPER_NoMap, SCALER_Scale<true, READER_Compressed> >(target, targetRect, scaledPosition, scaleX, scaleY);
-	} else {
-		render<MAPPER_NoMap, SCALER_Scale<false, READER_Compressed> >(target, targetRect, scaledPosition, scaleX, scaleY);
-	}
+	debug("scaleDraw");
+	dummyFill(target, targetRect);
 }
 
 void CelObj::scaleDrawUncomp(Buffer &target, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition) const {
-	if (_drawMirrored) {
-		render<MAPPER_NoMap, SCALER_Scale<true, READER_Uncompressed> >(target, targetRect, scaledPosition, scaleX, scaleY);
-	} else {
-		render<MAPPER_NoMap, SCALER_Scale<false, READER_Uncompressed> >(target, targetRect, scaledPosition, scaleX, scaleY);
-	}
+	debug("scaleDrawUncomp");
+	dummyFill(target, targetRect);
 }
 
 void CelObj::drawHzFlipMap(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const {
@@ -700,19 +670,17 @@ void CelObj::drawUncompHzFlipMap(Buffer &target, const Common::Rect &targetRect,
 }
 
 void CelObj::scaleDrawMap(Buffer &target, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition) const {
-	if (_drawMirrored) {
+	if (_drawMirrored)
 		render<MAPPER_Map, SCALER_Scale<true, READER_Compressed> >(target, targetRect, scaledPosition, scaleX, scaleY);
-	} else {
+	else
 		render<MAPPER_Map, SCALER_Scale<false, READER_Compressed> >(target, targetRect, scaledPosition, scaleX, scaleY);
-	}
 }
 
 void CelObj::scaleDrawUncompMap(Buffer &target, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition) const {
-	if (_drawMirrored) {
+	if (_drawMirrored)
 		render<MAPPER_Map, SCALER_Scale<true, READER_Uncompressed> >(target, targetRect, scaledPosition, scaleX, scaleY);
-	} else {
+	else
 		render<MAPPER_Map, SCALER_Scale<false, READER_Uncompressed> >(target, targetRect, scaledPosition, scaleX, scaleY);
-	}
 }
 
 void CelObj::drawNoFlipNoMD(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const {
@@ -747,16 +715,14 @@ void CelObj::scaleDrawNoMD(Buffer &target, const Ratio &scaleX, const Ratio &sca
 }
 
 void CelObj::scaleDrawUncompNoMD(Buffer &target, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition) const {
-	if (_drawMirrored) {
+	if (_drawMirrored)
 		render<MAPPER_NoMD, SCALER_Scale<true, READER_Uncompressed> >(target, targetRect, scaledPosition, scaleX, scaleY);
-	} else {
+	else
 		render<MAPPER_NoMD, SCALER_Scale<false, READER_Uncompressed> >(target, targetRect, scaledPosition, scaleX, scaleY);
-	}
 }
 
 #pragma mark -
 #pragma mark CelObjView
-
 CelObjView::CelObjView(const GuiResourceId viewId, const int16 loopNo, const int16 celNo) {
 	_info.type = kCelTypeView;
 	_info.resourceId = viewId;
@@ -874,12 +840,8 @@ CelObjView::CelObjView(const GuiResourceId viewId, const int16 loopNo, const int
 bool CelObjView::analyzeUncompressedForRemap() const {
 	byte *pixels = getResPointer() + READ_SCI11ENDIAN_UINT32(getResPointer() + _celHeaderOffset + 24);
 	for (int i = 0; i < _width * _height; ++i) {
-		const byte pixel = pixels[i];
-		if (
-			pixel >= g_sci->_gfxRemap32->getStartColor() &&
-			pixel <= g_sci->_gfxRemap32->getEndColor() &&
-			pixel != _transparentColor
-		) {
+		byte pixel = pixels[i];
+		if (pixel >= g_sci->_gfxRemap32->getStartColor() && pixel <= g_sci->_gfxRemap32->getEndColor() && pixel != _transparentColor) {
 			return true;
 		}
 	}
@@ -891,12 +853,8 @@ bool CelObjView::analyzeForRemap() const {
 	for (int y = 0; y < _height; y++) {
 		const byte *curRow = reader.getRow(y);
 		for (int x = 0; x < _width; x++) {
-			const byte pixel = curRow[x];
-			if (
-				pixel >= g_sci->_gfxRemap32->getStartColor() &&
-				pixel <= g_sci->_gfxRemap32->getEndColor() &&
-				pixel != _transparentColor
-			) {
+			byte pixel = curRow[x];
+			if (pixel >= g_sci->_gfxRemap32->getStartColor() && pixel <= g_sci->_gfxRemap32->getEndColor() && pixel != _transparentColor) {
 				return true;
 			}
 		}
@@ -923,7 +881,6 @@ byte *CelObjView::getResPointer() const {
 
 #pragma mark -
 #pragma mark CelObjPic
-
 CelObjPic::CelObjPic(const GuiResourceId picId, const int16 celNo) {
 	_info.type = kCelTypePic;
 	_info.resourceId = picId;
@@ -1045,7 +1002,6 @@ byte *CelObjPic::getResPointer() const {
 
 #pragma mark -
 #pragma mark CelObjMem
-
 CelObjMem::CelObjMem(const reg_t bitmapObject) {
 	_info.type = kCelTypeMem;
 	_info.bitmap = bitmapObject;
@@ -1075,7 +1031,6 @@ byte *CelObjMem::getResPointer() const {
 
 #pragma mark -
 #pragma mark CelObjColor
-
 CelObjColor::CelObjColor(const uint8 color, const int16 width, const int16 height) {
 	_info.type = kCelTypeColor;
 	_info.color = color;
diff --git a/engines/sci/graphics/frameout.cpp b/engines/sci/graphics/frameout.cpp
index fd37020..5771ab5 100644
--- a/engines/sci/graphics/frameout.cpp
+++ b/engines/sci/graphics/frameout.cpp
@@ -42,13 +42,15 @@
 #include "sci/graphics/coordadjuster.h"
 #include "sci/graphics/compare.h"
 #include "sci/graphics/font.h"
+#include "sci/graphics/view.h"
 #include "sci/graphics/screen.h"
 #include "sci/graphics/paint32.h"
 #include "sci/graphics/palette32.h"
+#include "sci/graphics/picture.h"
+#include "sci/graphics/remap.h"
+#include "sci/graphics/text32.h"
 #include "sci/graphics/plane32.h"
-#include "sci/graphics/remap32.h"
 #include "sci/graphics/screen_item32.h"
-#include "sci/graphics/text32.h"
 #include "sci/graphics/frameout.h"
 #include "sci/video/robot_decoder.h"
 
@@ -1005,7 +1007,7 @@ void GfxFrameout::mergeToShowList(const Common::Rect &drawRect, RectList &showLi
 }
 
 void GfxFrameout::palMorphFrameOut(const int8 *styleRanges, const ShowStyleEntry *showStyle) {
-	Palette sourcePalette(_palette->getNextPalette());
+	Palette sourcePalette(*_palette->getNextPalette());
 	alterVmap(sourcePalette, sourcePalette, -1, styleRanges);
 
 	int16 prevRoom = g_sci->getEngineState()->variables[VAR_GLOBAL][12].toSint16();
@@ -1045,7 +1047,7 @@ void GfxFrameout::palMorphFrameOut(const int8 *styleRanges, const ShowStyleEntry
 		drawScreenItemList(screenItemLists[i]);
 	}
 
-	Palette nextPalette(_palette->getNextPalette());
+	Palette nextPalette(*_palette->getNextPalette());
 
 	if (prevRoom < 1000) {
 		for (int i = 0; i < ARRAYSIZE(sourcePalette.colors); ++i) {
diff --git a/engines/sci/graphics/palette32.cpp b/engines/sci/graphics/palette32.cpp
index 0840e82..6844011 100644
--- a/engines/sci/graphics/palette32.cpp
+++ b/engines/sci/graphics/palette32.cpp
@@ -28,7 +28,7 @@
 #include "sci/event.h"
 #include "sci/resource.h"
 #include "sci/graphics/palette32.h"
-#include "sci/graphics/remap32.h"
+#include "sci/graphics/remap.h"
 #include "sci/graphics/screen.h"
 
 namespace Sci {
@@ -78,6 +78,10 @@ inline void mergePaletteInternal(Palette *const to, const Palette *const from) {
 	}
 }
 
+const Palette *GfxPalette32::getNextPalette() const {
+	return &_nextPalette;
+}
+
 void GfxPalette32::submit(Palette &palette) {
 	// TODO: The resource manager in SCI32 retains raw data of palettes from
 	// the ResourceManager (ResourceMgr) through SegManager (MemoryMgr), and
@@ -174,6 +178,49 @@ void GfxPalette32::set(Palette *newPalette, bool force, bool forceRealMerge) {
 	submit(*newPalette);
 }
 
+// In SCI32 engine this method is SOLPalette::Match(Rgb24 *, int, int *, int *)
+// and is used by Remap
+// TODO: Anything that calls GfxPalette::matchColor(int, int, int) is going to
+// match using an algorithm from SCI16 engine right now. This needs to be
+// corrected in the future so either nothing calls
+// GfxPalette::matchColor(int, int, int), or it is fixed to match the other
+// SCI32 algorithms.
+int16 GfxPalette32::matchColor(const byte r, const byte g, const byte b, const int defaultDifference, int &lastCalculatedDifference, const bool *const matchTable) {
+	int16 bestIndex = -1;
+	int bestDifference = 0xFFFFF;
+	int difference = defaultDifference;
+
+	// SQ6 DOS really does check only the first 236 entries
+	for (int i = 0, channelDifference; i < 236; ++i) {
+		if (matchTable[i] == 0) {
+			continue;
+		}
+
+		difference = _sysPalette.colors[i].r - r;
+		difference *= difference;
+		if (bestDifference <= difference) {
+			continue;
+		}
+		channelDifference = _sysPalette.colors[i].g - g;
+		difference += channelDifference * channelDifference;
+		if (bestDifference <= difference) {
+			continue;
+		}
+		channelDifference = _sysPalette.colors[i].b - b;
+		difference += channelDifference * channelDifference;
+		if (bestDifference <= difference) {
+			continue;
+		}
+		bestDifference = difference;
+		bestIndex = i;
+	}
+
+	// NOTE: This value is only valid if the last index to
+	// perform a difference calculation was the best index
+	lastCalculatedDifference = difference;
+	return bestIndex;
+}
+
 bool GfxPalette32::updateForFrame() {
 	applyAll();
 	_versionUpdated = false;
diff --git a/engines/sci/graphics/palette32.h b/engines/sci/graphics/palette32.h
index 7dda53e..1902d00 100644
--- a/engines/sci/graphics/palette32.h
+++ b/engines/sci/graphics/palette32.h
@@ -113,12 +113,12 @@ private:
 
 public:
 	virtual void saveLoadWithSerializer(Common::Serializer &s) override;
-	inline const Palette &getNextPalette() const { return _nextPalette; };
-	inline const Palette &getCurrentPalette() const { return _sysPalette; };
+	const Palette *getNextPalette() const;
 
 	bool kernelSetFromResource(GuiResourceId resourceId, bool force) override;
 	int16 kernelFindColor(uint16 r, uint16 g, uint16 b) override;
 	void set(Palette *newPalette, bool force, bool forceRealMerge = false) override;
+	int16 matchColor(const byte matchRed, const byte matchGreen, const byte matchBlue, const int defaultDifference, int &lastCalculatedDifference, const bool *const matchTable);
 
 	/**
 	 * Submits a palette to display. Entries marked as “used” in the
@@ -240,11 +240,6 @@ private:
 	 * According to SCI engine code, when two cyclers overlap,
 	 * a fatal error has occurred and the engine will display
 	 * an error and then exit.
-	 *
-	 * The cycle map is also by the color remapping system to
-	 * avoid attempting to remap to palette entries that are
-	 * cycling (so won't be the expected color once the cycler
-	 * runs again).
 	 */
 	bool _cycleMap[256];
 	inline void clearCycleMap(uint16 fromColor, uint16 numColorsToClear);
@@ -262,7 +257,7 @@ public:
 	void cycleAllOff();
 	void applyAllCycles();
 	void applyCycles();
-	inline const bool *getCycleMap() const { return _cycleMap; }
+	const bool *getCyclemap() { return _cycleMap; }
 
 #pragma mark -
 #pragma mark Fading
diff --git a/engines/sci/graphics/plane32.cpp b/engines/sci/graphics/plane32.cpp
index 175875c..6f5ad42 100644
--- a/engines/sci/graphics/plane32.cpp
+++ b/engines/sci/graphics/plane32.cpp
@@ -27,7 +27,7 @@
 #include "sci/graphics/frameout.h"
 #include "sci/graphics/lists32.h"
 #include "sci/graphics/plane32.h"
-#include "sci/graphics/remap32.h"
+#include "sci/graphics/remap.h"
 #include "sci/graphics/screen.h"
 #include "sci/graphics/screen_item32.h"
 
diff --git a/engines/sci/graphics/remap.cpp b/engines/sci/graphics/remap.cpp
index 2abf03e..ff49e52 100644
--- a/engines/sci/graphics/remap.cpp
+++ b/engines/sci/graphics/remap.cpp
@@ -21,23 +21,31 @@
  */
 
 #include "sci/sci.h"
+#include "sci/resource.h"
 #include "sci/graphics/palette.h"
+#include "sci/graphics/palette32.h"
 #include "sci/graphics/remap.h"
 #include "sci/graphics/screen.h"
 
 namespace Sci {
 
+#pragma mark -
+#pragma mark SCI16 remapping (QFG4 demo)
+
 GfxRemap::GfxRemap(GfxPalette *palette)
 	: _palette(palette) {
 	_remapOn = false;
 	resetRemapping();
 }
 
+GfxRemap::~GfxRemap() {
+}
+
 byte GfxRemap::remapColor(byte remappedColor, byte screenColor) {
 	assert(_remapOn);
-	if (_remappingType[remappedColor] == kRemapByRange)
+	if (_remappingType[remappedColor] == kRemappingByRange)
 		return _remappingByRange[screenColor];
-	else if (_remappingType[remappedColor] == kRemapByPercent)
+	else if (_remappingType[remappedColor] == kRemappingByPercent)
 		return _remappingByPercent[screenColor];
 	else
 		error("remapColor(): Color %d isn't remapped", remappedColor);
@@ -50,7 +58,7 @@ void GfxRemap::resetRemapping() {
 	_remappingPercentToSet = 0;
 
 	for (int i = 0; i < 256; i++) {
-		_remappingType[i] = kRemapNone;
+		_remappingType[i] = kRemappingNone;
 		_remappingByPercent[i] = i;
 		_remappingByRange[i] = i;
 	}
@@ -72,7 +80,7 @@ void GfxRemap::setRemappingPercent(byte color, byte percent) {
 		_remappingByPercent[i] = _palette->kernelFindColor(r, g, b);
 	}
 
-	_remappingType[color] = kRemapByPercent;
+	_remappingType[color] = kRemappingByPercent;
 }
 
 void GfxRemap::setRemappingRange(byte color, byte from, byte to, byte base) {
@@ -82,7 +90,7 @@ void GfxRemap::setRemappingRange(byte color, byte from, byte to, byte base) {
 		_remappingByRange[i] = i + base;
 	}
 
-	_remappingType[color] = kRemapByRange;
+	_remappingType[color] = kRemappingByRange;
 }
 
 void GfxRemap::updateRemapping() {
@@ -96,4 +104,277 @@ void GfxRemap::updateRemapping() {
 		}
 	}
 }
+
+#pragma mark -
+#pragma mark SCI32 remapping
+
+#ifdef ENABLE_SCI32
+
+GfxRemap32::GfxRemap32(GfxPalette32 *palette) : _palette(palette) {
+	for (int i = 0; i < REMAP_COLOR_COUNT; i++)
+		_remaps[i] = RemapParams(0, 0, 0, 0, 100, kRemappingNone);
+	_noMapStart = _noMapCount = 0;
+	_update = false;
+	_remapCount = 0;
+
+	// The remap range was 245 - 254 in SCI2, but was changed to 235 - 244 in SCI21 middle.
+	// All versions of KQ7 are using the older remap range semantics.
+	_remapEndColor = (getSciVersion() >= SCI_VERSION_2_1_MIDDLE || g_sci->getGameId() == GID_KQ7) ? 244 : 254;
+}
+
+void GfxRemap32::remapOff(byte color) {
+	if (!color) {
+		for (int i = 0; i < REMAP_COLOR_COUNT; i++)
+			_remaps[i] = RemapParams(0, 0, 0, 0, 100, kRemappingNone);
+
+		_remapCount = 0;
+	} else {
+		assert(_remapEndColor - color >= 0 && _remapEndColor - color < REMAP_COLOR_COUNT);
+		const byte index = _remapEndColor - color;
+		_remaps[index] = RemapParams(0, 0, 0, 0, 100, kRemappingNone);
+		_remapCount--;
+	}
+
+	_update = true;
+}
+
+void GfxRemap32::setRemappingRange(byte color, byte from, byte to, byte base) {
+	assert(_remapEndColor - color >= 0 && _remapEndColor - color < REMAP_COLOR_COUNT);
+	_remaps[_remapEndColor - color] = RemapParams(from, to, base, 0, 100, kRemappingByRange);
+	initColorArrays(_remapEndColor - color);
+	_remapCount++;
+	_update = true;
+}
+
+void GfxRemap32::setRemappingPercent(byte color, byte percent) {
+	assert(_remapEndColor - color >= 0 && _remapEndColor - color < REMAP_COLOR_COUNT);
+	_remaps[_remapEndColor - color] = RemapParams(0, 0, 0, 0, percent, kRemappingByPercent);
+	initColorArrays(_remapEndColor - color);
+	_remapCount++;
+	_update = true;
+}
+
+void GfxRemap32::setRemappingToGray(byte color, byte gray) {
+	assert(_remapEndColor - color >= 0 && _remapEndColor - color < REMAP_COLOR_COUNT);
+	_remaps[_remapEndColor - color] = RemapParams(0, 0, 0, gray, 100, kRemappingToGray);
+	initColorArrays(_remapEndColor - color);
+	_remapCount++;
+	_update = true;
+}
+
+void GfxRemap32::setRemappingToPercentGray(byte color, byte gray, byte percent) {
+	assert(_remapEndColor - color >= 0 && _remapEndColor - color < REMAP_COLOR_COUNT);
+	_remaps[_remapEndColor - color] = RemapParams(0, 0, 0, gray, percent, kRemappingToPercentGray);
+	initColorArrays(_remapEndColor - color);
+	_remapCount++;
+	_update = true;
+}
+
+void GfxRemap32::setNoMatchRange(byte from, byte count) {
+	_noMapStart = from;
+	_noMapCount = count;
+}
+
+bool GfxRemap32::remapEnabled(byte color) const {
+	assert(_remapEndColor - color >= 0 && _remapEndColor - color < REMAP_COLOR_COUNT);
+	const byte index = _remapEndColor - color;
+	return (_remaps[index].type != kRemappingNone);
+}
+
+byte GfxRemap32::remapColor(byte color, byte target) {
+	assert(_remapEndColor - color >= 0 && _remapEndColor - color < REMAP_COLOR_COUNT);
+	const byte index = _remapEndColor - color;
+	if (_remaps[index].type != kRemappingNone)
+		return _remaps[index].remap[target];
+	else
+		return target;
+}
+
+void GfxRemap32::initColorArrays(byte index) {
+	Palette *curPalette = &_palette->_sysPalette;
+	RemapParams *curRemap = &_remaps[index];
+
+	memcpy(curRemap->curColor, curPalette->colors, NON_REMAPPED_COLOR_COUNT * sizeof(Color));
+	memcpy(curRemap->targetColor, curPalette->colors, NON_REMAPPED_COLOR_COUNT * sizeof(Color));
+}
+
+bool GfxRemap32::updateRemap(byte index, bool palChanged) {
+	int result;
+	RemapParams *curRemap = &_remaps[index];
+	const Palette *curPalette = &_palette->_sysPalette;
+	const Palette *nextPalette = _palette->getNextPalette();
+	bool changed = false;
+
+	if (!_update && !palChanged)
+		return false;
+
+	Common::fill(_targetChanged, _targetChanged + NON_REMAPPED_COLOR_COUNT, false);
+
+	switch (curRemap->type) {
+	case kRemappingNone:
+		return false;
+	case kRemappingByRange:
+		for (int i = 0; i < NON_REMAPPED_COLOR_COUNT; i++)  {
+			if (curRemap->from <= i && i <= curRemap->to)
+				result = i + curRemap->base;
+			else
+				result = i;
+
+			if (curRemap->remap[i] != result) {
+				changed = true;
+				curRemap->remap[i] = result;
+			}
+
+			curRemap->colorChanged[i] = true;
+		}
+		return changed;
+	case kRemappingByPercent:
+		for (int i = 1; i < NON_REMAPPED_COLOR_COUNT; i++) {
+			// NOTE: This method uses nextPalette instead of curPalette
+			Color color = nextPalette->colors[i];
+
+			if (curRemap->curColor[i] != color) {
+				curRemap->colorChanged[i] = true;
+				curRemap->curColor[i] = color;
+			}
+
+			if (curRemap->percent != curRemap->oldPercent || curRemap->colorChanged[i])  {
+				byte red = CLIP<byte>(color.r * curRemap->percent / 100, 0, 255);
+				byte green = CLIP<byte>(color.g * curRemap->percent / 100, 0, 255);
+				byte blue = CLIP<byte>(color.b * curRemap->percent / 100, 0, 255);
+				byte used = curRemap->targetColor[i].used;
+
+				Color newColor = { used, red, green, blue };
+				if (curRemap->targetColor[i] != newColor)  {
+					_targetChanged[i] = true;
+					curRemap->targetColor[i] = newColor;
+				}
+			}
+		}
+		
+		changed = applyRemap(index);
+		Common::fill(curRemap->colorChanged, curRemap->colorChanged + NON_REMAPPED_COLOR_COUNT, false);
+		curRemap->oldPercent = curRemap->percent;
+		return changed;
+	case kRemappingToGray:
+		for (int i = 1; i < NON_REMAPPED_COLOR_COUNT; i++) {
+			Color color = curPalette->colors[i];
+
+			if (curRemap->curColor[i] != color) {
+				curRemap->colorChanged[i] = true;
+				curRemap->curColor[i] = color;
+			}
+
+			if (curRemap->gray != curRemap->oldGray || curRemap->colorChanged[i])  {
+				byte lumosity = ((color.r * 77) + (color.g * 151) + (color.b * 28)) >> 8;
+				byte red = CLIP<byte>(color.r - ((color.r - lumosity) * curRemap->gray / 100), 0, 255);
+				byte green = CLIP<byte>(color.g - ((color.g - lumosity) * curRemap->gray / 100), 0, 255);
+				byte blue = CLIP<byte>(color.b - ((color.b - lumosity) * curRemap->gray / 100), 0, 255);
+				byte used = curRemap->targetColor[i].used;
+
+				Color newColor = { used, red, green, blue };
+				if (curRemap->targetColor[i] != newColor)  {
+					_targetChanged[i] = true;
+					curRemap->targetColor[i] = newColor;
+				}
+			}
+		}
+
+		changed = applyRemap(index);
+		Common::fill(curRemap->colorChanged, curRemap->colorChanged + NON_REMAPPED_COLOR_COUNT, false);
+		curRemap->oldGray = curRemap->gray;
+		return changed;
+	case kRemappingToPercentGray:
+		for (int i = 1; i < NON_REMAPPED_COLOR_COUNT; i++) {
+			Color color = curPalette->colors[i];
+
+			if (curRemap->curColor[i] != color) {
+				curRemap->colorChanged[i] = true;
+				curRemap->curColor[i] = color;
+			}
+
+			if (curRemap->percent != curRemap->oldPercent || curRemap->gray != curRemap->oldGray || curRemap->colorChanged[i])  {
+				byte lumosity = ((color.r * 77) + (color.g * 151) + (color.b * 28)) >> 8;
+				lumosity = lumosity * curRemap->percent / 100;
+				byte red = CLIP<byte>(color.r - ((color.r - lumosity) * curRemap->gray / 100), 0, 255);
+				byte green = CLIP<byte>(color.g - ((color.g - lumosity) * curRemap->gray / 100), 0, 255);
+				byte blue = CLIP<byte>(color.b - ((color.b - lumosity) * curRemap->gray / 100), 0, 255);
+				byte used = curRemap->targetColor[i].used;
+
+				Color newColor = { used, red, green, blue };
+				if (curRemap->targetColor[i] != newColor)  {
+					_targetChanged[i] = true;
+					curRemap->targetColor[i] = newColor;
+				}
+			}
+		}
+
+		changed = applyRemap(index);
+		Common::fill(curRemap->colorChanged, curRemap->colorChanged + NON_REMAPPED_COLOR_COUNT, false);
+		curRemap->oldPercent = curRemap->percent;
+		curRemap->oldGray = curRemap->gray;
+		return changed;
+	default:
+		return false;
+	}
+}
+
+static int colorDistance(Color a, Color b) {
+	int rDiff = (a.r - b.r) * (a.r - b.r);
+	int gDiff = (a.g - b.g) * (a.g - b.g);
+	int bDiff = (a.b - b.b) * (a.b - b.b);
+	return rDiff + gDiff + bDiff;
+}
+
+bool GfxRemap32::applyRemap(byte index) {
+	RemapParams *curRemap = &_remaps[index];
+	const bool *cycleMap = _palette->getCyclemap();
+	bool unmappedColors[NON_REMAPPED_COLOR_COUNT];
+	bool changed = false;
+
+	Common::fill(unmappedColors, unmappedColors + NON_REMAPPED_COLOR_COUNT, false);
+	if (_noMapCount)
+		Common::fill(unmappedColors + _noMapStart, unmappedColors + _noMapStart + _noMapCount, true);
+
+	for (int i = 0; i < NON_REMAPPED_COLOR_COUNT; i++)  {
+		if (cycleMap[i])
+			unmappedColors[i] = true;
+	}
+
+	for (int i = 1; i < NON_REMAPPED_COLOR_COUNT; i++)  {
+		Color targetColor = curRemap->targetColor[i];
+		bool colorChanged = curRemap->colorChanged[curRemap->remap[i]];
+
+		if (!_targetChanged[i] && !colorChanged)
+			continue;
+
+		if (_targetChanged[i] && colorChanged)
+			if (curRemap->distance[i] < 100 && colorDistance(targetColor, curRemap->curColor[curRemap->remap[i]]) <= curRemap->distance[i])
+				continue;
+
+		int diff = 0;
+		int16 result = _palette->matchColor(targetColor.r, targetColor.g, targetColor.b, curRemap->distance[i], diff, unmappedColors);
+		if (result != -1 && curRemap->remap[i] != result)  {
+			changed = true;
+			curRemap->remap[i] = result;
+			curRemap->distance[i] = diff;
+		}
+	}
+
+	return changed;
+}
+
+bool GfxRemap32::remapAllTables(bool palChanged) {
+	bool changed = false;
+
+	for (int i = 0; i < REMAP_COLOR_COUNT; i++) {
+		changed |= updateRemap(i, palChanged);
+	}
+
+	_update = false;
+	return changed;
+}
+
+#endif
+
 } // End of namespace Sci
diff --git a/engines/sci/graphics/remap.h b/engines/sci/graphics/remap.h
index 98177f6..d012568 100644
--- a/engines/sci/graphics/remap.h
+++ b/engines/sci/graphics/remap.h
@@ -24,36 +24,42 @@
 #define SCI_GRAPHICS_REMAP_H
 
 #include "common/array.h"
-#include "common/serializer.h"
+#include "sci/graphics/helpers.h"
 
 namespace Sci {
 
 class GfxScreen;
 
+enum ColorRemappingType {
+	kRemappingNone = 0,
+	kRemappingByRange = 1,
+	kRemappingByPercent = 2,
+	kRemappingToGray = 3,
+	kRemappingToPercentGray = 4
+};
+
+#define REMAP_COLOR_COUNT 9
+#define NON_REMAPPED_COLOR_COUNT 236
+
 /**
- * This class handles color remapping for the QFG4 demo.
+ * Remap class, handles color remapping
  */
 class GfxRemap {
-private:
-	enum ColorRemappingType {
-		kRemapNone = 0,
-		kRemapByRange = 1,
-		kRemapByPercent = 2
-	};
-
 public:
 	GfxRemap(GfxPalette *_palette);
+	~GfxRemap();
 
 	void resetRemapping();
 	void setRemappingPercent(byte color, byte percent);
 	void setRemappingRange(byte color, byte from, byte to, byte base);
 	bool isRemapped(byte color) const {
-		return _remapOn && (_remappingType[color] != kRemapNone);
+		return _remapOn && (_remappingType[color] != kRemappingNone);
 	}
 	byte remapColor(byte remappedColor, byte screenColor);
 	void updateRemapping();
 
 private:
+	GfxScreen *_screen;
 	GfxPalette *_palette;
 
 	bool _remapOn;
@@ -62,6 +68,87 @@ private:
 	byte _remappingByRange[256];
 	uint16 _remappingPercentToSet;
 };
+
+#ifdef ENABLE_SCI32
+
+struct RemapParams {
+	byte from;
+	byte to;
+	byte base;
+	byte gray;
+	byte oldGray;
+	byte percent;
+	byte oldPercent;
+	ColorRemappingType type;
+	Color curColor[256];
+	Color targetColor[256];
+	byte distance[256];
+	byte remap[256];
+	bool colorChanged[256];
+
+	RemapParams() {
+		from = to = base = gray = oldGray = percent = oldPercent = 0;
+		type = kRemappingNone;
+
+		// curColor and targetColor are initialized in GfxRemap32::initColorArrays
+		memset(curColor, 0, 256 * sizeof(Color));
+		memset(targetColor, 0, 256 * sizeof(Color));
+		memset(distance, 0, 256);
+		for (int i = 0; i < NON_REMAPPED_COLOR_COUNT; i++)
+			remap[i] = i;
+		Common::fill(colorChanged, colorChanged + ARRAYSIZE(colorChanged), true);
+	}
+
+	RemapParams(byte from_, byte to_, byte base_, byte gray_, byte percent_, ColorRemappingType type_) {
+		from = from_;
+		to = to_;
+		base = base_;
+		gray = oldGray = gray_;
+		percent = oldPercent = percent_;
+		type = type_;
+
+		// curColor and targetColor are initialized in GfxRemap32::initColorArrays
+		memset(curColor, 0, 256 * sizeof(Color));
+		memset(targetColor, 0, 256 * sizeof(Color));
+		memset(distance, 0, 256);
+		for (int i = 0; i < NON_REMAPPED_COLOR_COUNT; i++)
+			remap[i] = i;
+		Common::fill(colorChanged, colorChanged + ARRAYSIZE(colorChanged), true);
+	}
+};
+
+class GfxRemap32 {
+public:
+	GfxRemap32(GfxPalette32 *palette);
+	~GfxRemap32() {}
+
+	void remapOff(byte color);
+	void setRemappingRange(byte color, byte from, byte to, byte base);
+	void setRemappingPercent(byte color, byte percent);
+	void setRemappingToGray(byte color, byte gray);
+	void setRemappingToPercentGray(byte color, byte gray, byte percent);
+	void setNoMatchRange(byte from, byte count);
+	bool remapEnabled(byte color) const;
+	byte remapColor(byte color, byte target);
+	bool remapAllTables(bool palChanged);
+	int getRemapCount() const { return _remapCount; }
+	int getStartColor() const { return _remapEndColor - REMAP_COLOR_COUNT + 1; }
+	int getEndColor() const { return _remapEndColor; }
+private:
+	GfxPalette32 *_palette;
+	RemapParams _remaps[REMAP_COLOR_COUNT];
+	bool _update;
+	byte _noMapStart, _noMapCount;
+	bool _targetChanged[NON_REMAPPED_COLOR_COUNT];
+	byte _remapEndColor;
+	int _remapCount;
+
+	void initColorArrays(byte index);
+	bool applyRemap(byte index);
+	bool updateRemap(byte index, bool palChanged);
+};
+#endif
+
 } // End of namespace Sci
 
 #endif
diff --git a/engines/sci/graphics/remap32.cpp b/engines/sci/graphics/remap32.cpp
deleted file mode 100644
index d5a2362..0000000
--- a/engines/sci/graphics/remap32.cpp
+++ /dev/null
@@ -1,468 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "sci/sci.h"
-#include "sci/graphics/palette32.h"
-#include "sci/graphics/remap32.h"
-
-namespace Sci {
-
-#pragma mark SingleRemap
-
-void SingleRemap::reset() {
-	_lastPercent = 100;
-	_lastGray = 0;
-
-	const uint8 remapStartColor = g_sci->_gfxRemap32->getStartColor();
-	const Palette &currentPalette = g_sci->_gfxPalette32->getCurrentPalette();
-	for (uint i = 0; i < remapStartColor; ++i) {
-		const Color &color = currentPalette.colors[i];
-		_remapColors[i] = i;
-		_originalColors[i] = color;
-		_originalColorsChanged[i] = true;
-		_idealColors[i] = color;
-		_idealColorsChanged[i] = false;
-		_matchDistances[i] = 0;
-	}
-}
-
-bool SingleRemap::update() {
-	switch (_type) {
-	case kRemapNone:
-		break;
-	case kRemapByRange:
-		return updateRange();
-	case kRemapByPercent:
-		return updateBrightness();
-	case kRemapToGray:
-		return updateSaturation();
-	case kRemapToPercentGray:
-		return updateSaturationAndBrightness();
-	default:
-		error("Illegal remap type %d", _type);
-	}
-
-	return false;
-}
-
-bool SingleRemap::updateRange() {
-	const uint8 remapStartColor = g_sci->_gfxRemap32->getStartColor();
-	bool updated = false;
-
-	for (uint i = 0; i < remapStartColor; ++i) {
-		uint8 targetColor;
-		if (_from <= i && i <= _to) {
-			targetColor = i + _delta;
-		} else {
-			targetColor = i;
-		}
-
-		if (_remapColors[i] != targetColor) {
-			updated = true;
-			_remapColors[i] = targetColor;
-		}
-
-		_originalColorsChanged[i] = true;
-	}
-
-	return updated;
-}
-
-bool SingleRemap::updateBrightness() {
-	const uint8 remapStartColor = g_sci->_gfxRemap32->getStartColor();
-	const Palette &nextPalette = g_sci->_gfxPalette32->getNextPalette();
-	for (uint i = 1; i < remapStartColor; ++i) {
-		Color color(nextPalette.colors[i]);
-
-		if (_originalColors[i] != color) {
-			_originalColorsChanged[i] = true;
-			_originalColors[i] = color;
-		}
-
-		if (_percent != _lastPercent || _originalColorsChanged[i]) {
-			// NOTE: SSCI checked if percent was over 100 and only
-			// then clipped values, but we always unconditionally
-			// ensure the result is in the correct range
-			color.r = MIN(255, (uint16)color.r * _percent / 100);
-			color.g = MIN(255, (uint16)color.g * _percent / 100);
-			color.b = MIN(255, (uint16)color.b * _percent / 100);
-
-			if (_idealColors[i] != color) {
-				_idealColorsChanged[i] = true;
-				_idealColors[i] = color;
-			}
-		}
-	}
-
-	const bool updated = apply();
-	Common::fill(_originalColorsChanged, _originalColorsChanged + remapStartColor, false);
-	Common::fill(_idealColorsChanged, _idealColorsChanged + remapStartColor, false);
-	_lastPercent = _percent;
-	return updated;
-}
-
-bool SingleRemap::updateSaturation() {
-	const uint8 remapStartColor = g_sci->_gfxRemap32->getStartColor();
-	const Palette &currentPalette = g_sci->_gfxPalette32->getCurrentPalette();
-	for (uint i = 1; i < remapStartColor; ++i) {
-		Color color(currentPalette.colors[i]);
-		if (_originalColors[i] != color) {
-			_originalColorsChanged[i] = true;
-			_originalColors[i] = color;
-		}
-
-		if (_gray != _lastGray || _originalColorsChanged[i]) {
-			const int luminosity = (((color.r * 77) + (color.g * 151) + (color.b * 28)) >> 8) * _percent / 100;
-
-			color.r = MIN(255, color.r - ((color.r - luminosity) * _gray / 100));
-			color.g = MIN(255, color.g - ((color.g - luminosity) * _gray / 100));
-			color.b = MIN(255, color.b - ((color.b - luminosity) * _gray / 100));
-
-			if (_idealColors[i] != color) {
-				_idealColorsChanged[i] = true;
-				_idealColors[i] = color;
-			}
-		}
-	}
-
-	const bool updated = apply();
-	Common::fill(_originalColorsChanged, _originalColorsChanged + remapStartColor, false);
-	Common::fill(_idealColorsChanged, _idealColorsChanged + remapStartColor, false);
-	_lastGray = _gray;
-	return updated;
-}
-
-bool SingleRemap::updateSaturationAndBrightness() {
-	const uint8 remapStartColor = g_sci->_gfxRemap32->getStartColor();
-	const Palette &currentPalette = g_sci->_gfxPalette32->getCurrentPalette();
-	for (uint i = 1; i < remapStartColor; i++) {
-		Color color(currentPalette.colors[i]);
-		if (_originalColors[i] != color) {
-			_originalColorsChanged[i] = true;
-			_originalColors[i] = color;
-		}
-
-		if (_percent != _lastPercent || _gray != _lastGray || _originalColorsChanged[i]) {
-			const int luminosity = (((color.r * 77) + (color.g * 151) + (color.b * 28)) >> 8) * _percent / 100;
-
-			color.r = MIN(255, color.r - ((color.r - luminosity) * _gray) / 100);
-			color.g = MIN(255, color.g - ((color.g - luminosity) * _gray) / 100);
-			color.b = MIN(255, color.b - ((color.b - luminosity) * _gray) / 100);
-
-			if (_idealColors[i] != color) {
-				_idealColorsChanged[i] = true;
-				_idealColors[i] = color;
-			}
-		}
-	}
-
-	const bool updated = apply();
-	Common::fill(_originalColorsChanged, _originalColorsChanged + remapStartColor, false);
-	Common::fill(_idealColorsChanged, _idealColorsChanged + remapStartColor, false);
-	_lastPercent = _percent;
-	_lastGray = _gray;
-	return updated;
-}
-
-bool SingleRemap::apply() {
-	const GfxRemap32 *const gfxRemap32 = g_sci->_gfxRemap32;
-	const uint8 remapStartColor = gfxRemap32->getStartColor();
-
-	// Blocked colors are not allowed to be used as target
-	// colors for the remap
-	bool blockedColors[236];
-	Common::fill(blockedColors, blockedColors + remapStartColor, false);
-
-	const bool *const paletteCycleMap = g_sci->_gfxPalette32->getCycleMap();
-
-	const int16 blockedRangeCount = gfxRemap32->getBlockedRangeCount();
-	if (blockedRangeCount) {
-		const uint8 blockedRangeStart = gfxRemap32->getBlockedRangeStart();
-		Common::fill(blockedColors + blockedRangeStart, blockedColors + blockedRangeStart + blockedRangeCount, true);
-	}
-
-	for (uint i = 0; i < remapStartColor; ++i) {
-		if (paletteCycleMap[i]) {
-			blockedColors[i] = true;
-		}
-	}
-
-	// NOTE: SSCI did a loop over colors here to create a
-	// new array of updated, unblocked colors, but then
-	// never used it
-
-	bool updated = false;
-	for (uint i = 1; i < remapStartColor; ++i) {
-		int distance;
-
-		if (!_idealColorsChanged[i] && !_originalColorsChanged[_remapColors[i]]) {
-			continue;
-		}
-
-		if (
-			_idealColorsChanged[i] &&
-			_originalColorsChanged[_remapColors[i]] &&
-			_matchDistances[i] < 100 &&
-			colorDistance(_idealColors[i], _originalColors[_remapColors[i]]) <= _matchDistances[i]
-		) {
-			continue;
-		}
-
-		const int16 bestColor = matchColor(_idealColors[i], _matchDistances[i], distance, blockedColors);
-
-		if (bestColor != -1 && _remapColors[i] != bestColor) {
-			updated = true;
-			_remapColors[i] = bestColor;
-			_matchDistances[i] = distance;
-		}
-	}
-
-	return updated;
-}
-
-int SingleRemap::colorDistance(const Color &a, const Color &b) const {
-	int channelDistance = a.r - b.r;
-	int distance = channelDistance * channelDistance;
-	channelDistance = a.g - b.g;
-	distance += channelDistance * channelDistance;
-	channelDistance = a.b - b.b;
-	distance += channelDistance * channelDistance;
-	return distance;
-}
-
-int16 SingleRemap::matchColor(const Color &color, const int minimumDistance, int &outDistance, const bool *const blockedIndexes) const {
-	int16 bestIndex = -1;
-	int bestDistance = 0xFFFFF;
-	int distance = minimumDistance;
-	const Palette &nextPalette = g_sci->_gfxPalette32->getNextPalette();
-
-	for (uint i = 0, channelDistance; i < g_sci->_gfxRemap32->getStartColor(); ++i) {
-		if (blockedIndexes[i]) {
-			continue;
-		}
-
-		distance = nextPalette.colors[i].r - color.r;
-		distance *= distance;
-		if (bestDistance <= distance) {
-			continue;
-		}
-		channelDistance = nextPalette.colors[i].g - color.g;
-		distance += channelDistance * channelDistance;
-		if (bestDistance <= distance) {
-			continue;
-		}
-		channelDistance = nextPalette.colors[i].b - color.b;
-		distance += channelDistance * channelDistance;
-		if (bestDistance <= distance) {
-			continue;
-		}
-		bestDistance = distance;
-		bestIndex = i;
-	}
-
-	// This value is only valid if the last index to
-	// perform a distance calculation was the best index
-	outDistance = distance;
-	return bestIndex;
-}
-
-#pragma mark -
-#pragma mark GfxRemap32
-
-GfxRemap32::GfxRemap32() :
-	_needsUpdate(false),
-	_blockedRangeStart(0),
-	_blockedRangeCount(0),
-	_remapStartColor(236),
-	_numActiveRemaps(0) {
-	// The `_remapStartColor` seems to always be 236 in SSCI,
-	// but if it is ever changed then the various C-style
-	// member arrays hard-coded to 236 need to be changed to
-	// match the highest possible value of `_remapStartColor`
-	assert(_remapStartColor == 236);
-
-	if (getSciVersion() >= SCI_VERSION_2_1_MIDDLE || g_sci->getGameId() == GID_KQ7) {
-		_remaps.resize(9);
-	} else {
-		_remaps.resize(19);
-	}
-
-	_remapEndColor = _remapStartColor + _remaps.size() - 1;
-}
-
-void GfxRemap32::remapOff(const uint8 color) {
-	if (color == 0) {
-		remapAllOff();
-		return;
-	}
-
-	// NOTE: SSCI simply ignored invalid input values, but
-	// we at least give a warning so games can be investigated
-	// for script bugs
-	if (color < _remapStartColor || color > _remapEndColor) {
-		warning("GfxRemap32::remapOff: %d out of remap range", color);
-		return;
-	}
-
-	const uint8 index = _remapEndColor - color;
-	SingleRemap &singleRemap = _remaps[index];
-	singleRemap._type = kRemapNone;
-	--_numActiveRemaps;
-	_needsUpdate = true;
-}
-
-void GfxRemap32::remapAllOff() {
-	for (uint i = 0, len = _remaps.size(); i < len; ++i) {
-		_remaps[i]._type = kRemapNone;
-	}
-
-	_numActiveRemaps = 0;
-	_needsUpdate = true;
-}
-
-void GfxRemap32::remapByRange(const uint8 color, const int16 from, const int16 to, const int16 delta) {
-	// NOTE: SSCI simply ignored invalid input values, but
-	// we at least give a warning so games can be investigated
-	// for script bugs
-	if (color < _remapStartColor || color > _remapEndColor) {
-		warning("GfxRemap32::remapByRange: %d out of remap range", color);
-		return;
-	}
-
-	if (from < 0) {
-		warning("GfxRemap32::remapByRange: attempt to remap negative color %d", from);
-		return;
-	}
-
-	if (to >= _remapStartColor) {
-		warning("GfxRemap32::remapByRange: attempt to remap into the remap zone at %d", to);
-		return;
-	}
-
-	const uint8 index = _remapEndColor - color;
-	SingleRemap &singleRemap = _remaps[index];
-
-	if (singleRemap._type == kRemapNone) {
-		++_numActiveRemaps;
-		singleRemap.reset();
-	}
-
-	singleRemap._from = from;
-	singleRemap._to = to;
-	singleRemap._delta = delta;
-	singleRemap._type = kRemapByRange;
-	_needsUpdate = true;
-}
-
-void GfxRemap32::remapByPercent(const uint8 color, const int16 percent) {
-	// NOTE: SSCI simply ignored invalid input values, but
-	// we at least give a warning so games can be investigated
-	// for script bugs
-	if (color < _remapStartColor || color > _remapEndColor) {
-		warning("GfxRemap32::remapByPercent: %d out of remap range", color);
-		return;
-	}
-
-	const uint8 index = _remapEndColor - color;
-	SingleRemap &singleRemap = _remaps[index];
-
-	if (singleRemap._type == kRemapNone) {
-		++_numActiveRemaps;
-		singleRemap.reset();
-	}
-
-	singleRemap._percent = percent;
-	singleRemap._type = kRemapByPercent;
-	_needsUpdate = true;
-}
-
-void GfxRemap32::remapToGray(const uint8 color, const int8 gray) {
-	// NOTE: SSCI simply ignored invalid input values, but
-	// we at least give a warning so games can be investigated
-	// for script bugs
-	if (color < _remapStartColor || color > _remapEndColor) {
-		warning("GfxRemap32::remapToGray: %d out of remap range", color);
-		return;
-	}
-
-	if (gray < 0 || gray > 100) {
-		error("RemapToGray percent out of range; gray = %d", gray);
-	}
-
-	const uint8 index = _remapEndColor - color;
-	SingleRemap &singleRemap = _remaps[index];
-
-	if (singleRemap._type == kRemapNone) {
-		++_numActiveRemaps;
-		singleRemap.reset();
-	}
-
-	singleRemap._gray = gray;
-	singleRemap._type = kRemapToGray;
-	_needsUpdate = true;
-}
-
-void GfxRemap32::remapToPercentGray(const uint8 color, const int16 gray, const int16 percent) {
-	// NOTE: SSCI simply ignored invalid input values, but
-	// we at least give a warning so games can be investigated
-	// for script bugs
-	if (color < _remapStartColor || color > _remapEndColor) {
-		warning("GfxRemap32::remapToPercentGray: %d out of remap range", color);
-		return;
-	}
-
-	const uint8 index = _remapEndColor - color;
-	SingleRemap &singleRemap = _remaps[index];
-
-	if (singleRemap._type == kRemapNone) {
-		++_numActiveRemaps;
-		singleRemap.reset();
-	}
-
-	singleRemap._percent = percent;
-	singleRemap._gray = gray;
-	singleRemap._type = kRemapToPercentGray;
-	_needsUpdate = true;
-}
-
-void GfxRemap32::blockRange(const uint8 from, const int16 count) {
-	_blockedRangeStart = from;
-	_blockedRangeCount = count;
-}
-
-bool GfxRemap32::remapAllTables(const bool paletteUpdated) {
-	if (!_needsUpdate && !paletteUpdated) {
-		return false;
-	}
-
-	bool updated = false;
-
-	for (SingleRemapsList::iterator it = _remaps.begin(); it != _remaps.end(); ++it) {
-		if (it->_type != kRemapNone) {
-			updated |= it->update();
-		}
-	}
-
-	_needsUpdate = false;
-	return updated;
-}
-} // End of namespace Sci
diff --git a/engines/sci/graphics/remap32.h b/engines/sci/graphics/remap32.h
deleted file mode 100644
index 5f629d7..0000000
--- a/engines/sci/graphics/remap32.h
+++ /dev/null
@@ -1,400 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#ifndef SCI_GRAPHICS_REMAP32_H
-#define SCI_GRAPHICS_REMAP32_H
-
-#include "common/algorithm.h"
-#include "common/array.h"
-#include "common/scummsys.h"
-#include "sci/graphics/helpers.h"
-
-namespace Sci {
-class GfxPalette32;
-
-enum RemapType {
-	kRemapNone = 0,
-	kRemapByRange = 1,
-	kRemapByPercent = 2,
-	kRemapToGray = 3,
-	kRemapToPercentGray = 4
-};
-
-#pragma mark -
-#pragma mark SingleRemap
-
-/**
- * SingleRemap objects each manage one remapping operation.
- */
-class SingleRemap {
-public:
-	SingleRemap() : _type(kRemapNone) {}
-
-	/**
-	 * The type of remap.
-	 */
-	RemapType _type;
-
-	/**
-	 * The first color that should be shifted by a range
-	 * remap.
-	 */
-	uint8 _from;
-
-	/**
-	 * The last color that should be shifted a range remap.
-	 */
-	uint8 _to;
-
-	/**
-	 * The direction and amount that the colors should be
-	 * shifted in a range remap.
-	 */
-	int16 _delta;
-
-	/**
-	 * The difference in brightness that should be
-	 * applied by a brightness (percent) remap.
-	 *
-	 * This value may be be greater than 100, in
-	 * which case the color will be oversaturated.
-	 */
-	int16 _percent;
-
-	/**
-	 * The amount of desaturation that should be
-	 * applied by a saturation (gray) remap, where
-	 * 0 is full saturation and 100 is full
-	 * desaturation.
-	 */
-	uint8 _gray;
-
-	/**
-	 * The final array used by CelObj renderers to composite
-	 * remapped pixels to the screen buffer.
-	 *
-	 * Here is how it works:
-	 *
-	 * The source bitmap being rendered will have pixels
-	 * within the remap range (236-245 or 236-254), and the
-	 * target buffer will have colors in the non-remapped
-	 * range (0-235).
-	 *
-	 * To arrive at the correct color, first the source
-	 * pixel is used to look up the correct SingleRemap for
-	 * that pixel. Then, the final composited color is
-	 * looked up in this array using the target's pixel
-	 * color. In other words,
-	 * `target = _remaps[remapEndColor - source].remapColors[target]`.
-	 */
-	uint8 _remapColors[236];
-
-	/**
-	 * Resets this SingleRemap's color information to
-	 * default values.
-	 */
-	void reset();
-
-	/**
-	 * Recalculates and reapplies remap colors to the
-	 * `_remapColors` array.
-	 */
-	bool update();
-
-private:
-	/**
-	 * The previous brightness value. Used to
-	 * determine whether or not targetColors needs
-	 * to be updated.
-	 */
-	int16 _lastPercent;
-
-	/**
-	 * The previous saturation value. Used to
-	 * determine whether or not targetColors needs
-	 * to be updated.
-	 */
-	uint8 _lastGray;
-
-	/**
-	 * The colors from the current GfxPalette32 palette
-	 * before this SingleRemap is applied.
-	 */
-	Color _originalColors[236];
-
-	/**
-	 * Map of colors that changed in `_originalColors`
-	 * when this SingleRemap was updated. This map is
-	 * transient and gets reset to `false` after the
-	 * SingleRemap finishes updating.
-	 */
-	bool _originalColorsChanged[236];
-
-	/**
-	 * The ideal target RGB color values for each generated
-	 * remap color.
-	 */
-	Color _idealColors[236];
-
-	/**
-	 * Map of colors that changed in `_idealColors` when
-	 * this SingleRemap was updated. This map is transient
-	 * and gets reset to `false` after the SingleRemap
-	 * finishes applying.
-	 */
-	bool _idealColorsChanged[236];
-
-	/**
-	 * When applying a SingleRemap, finding an appropriate
-	 * color in the palette is the responsibility of a
-	 * distance function. Once a match is found, the
-	 * distance of that match is stored here so that the
-	 * next time the SingleRemap is applied, it can check
-	 * the distance from the previous application and avoid
-	 * triggering an expensive redraw of the entire screen
-	 * if the new palette value only changed slightly.
-	 */
-	int _matchDistances[236];
-
-	/**
-	 * Computes the final target values for a range remap
-	 * and applies them directly to the `_remaps` map.
-	 *
-	 * @note Was ByRange in SSCI.
-	 */
-	bool updateRange();
-
-	/**
-	 * Computes the intermediate target values for a
-	 * brightness remap and applies them indirectly via
-	 * the `apply` method.
-	 *
-	 * @note Was ByPercent in SSCI.
-	 */
-	bool updateBrightness();
-
-	/**
-	 * Computes the intermediate target values for a
-	 * saturation remap and applies them indirectly via
-	 * the `apply` method.
-	 *
-	 * @note Was ToGray in SSCI.
-	 */
-	bool updateSaturation();
-
-	/**
-	 * Computes the intermediate target values for a
-	 * saturation + brightness bitmap and applies them
-	 * indirectly via the `apply` method.
-	 *
-	 * @note Was ToPercentGray in SSCI.
-	 */
-	bool updateSaturationAndBrightness();
-
-	/**
-	 * Computes and applies the final values to the
-	 * `_remaps` map.
-	 *
-	 * @note In SSCI, a boolean array of changed values
-	 * was passed into this method, but this was done by
-	 * creating arrays on the stack in the caller. Instead
-	 * of doing this, we simply add another member property
-	 * `_idealColorsChanged` and use that instead.
-	 */
-	bool apply();
-
-	/**
-	 * Calculates the square distance of two colors.
-	 *
-	 * @note In SSCI this method is Rgb24::Dist, but it is
-	 * only used by SingleRemap.
-	 */
-	int colorDistance(const Color &a, const Color &b) const;
-
-	/**
-	 * Finds the closest index in the next palette matching
-	 * the given RGB color. Returns -1 if no match can be
-	 * found that is closer than `minimumDistance`.
-	 *
-	 * @note In SSCI, this method is SOLPalette::Match, but
-	 * this particular signature is only used by
-	 * SingleRemap.
-	 */
-	int16 matchColor(const Color &color, const int minimumDistance, int &outDistance, const bool *const blockedIndexes) const;
-};
-
-#pragma mark -
-#pragma mark GfxRemap32
-
-/**
- * This class provides color remapping support for SCI32
- * games.
- */
-class GfxRemap32 : public Common::Serializable {
-public:
-	GfxRemap32();
-
-	void saveLoadWithSerializer(Common::Serializer &s);
-
-	inline uint8 getRemapCount() const { return _numActiveRemaps; }
-	inline uint8 getStartColor() const { return _remapStartColor; }
-	inline uint8 getEndColor() const { return _remapEndColor; }
-	inline uint8 getBlockedRangeStart() const { return _blockedRangeStart; }
-	inline int16 getBlockedRangeCount() const { return _blockedRangeCount; }
-
-	/**
-	 * Turns off remapping of the given color. If `color` is
-	 * 0, all remaps are turned off.
-	 */
-	void remapOff(const uint8 color);
-
-	/**
-	 * Turns off all color remaps.
-	 */
-	void remapAllOff();
-
-	/**
-	 * Configures a SingleRemap for the remap color `color`.
-	 * The SingleRemap will shift palette colors between
-	 * `from` and `to` (inclusive) by `delta` palette
-	 * entries when the remap is applied.
-	 */
-	void remapByRange(const uint8 color, const int16 from, const int16 to, const int16 delta);
-
-	/**
-	 * Configures a SingleRemap for the remap color `color`
-	 * to modify the brightness of remapped colors by
-	 * `percent`.
-	 */
-	void remapByPercent(const uint8 color, const int16 percent);
-
-	/**
-	 * Configures a SingleRemap for the remap color `color`
-	 * to modify the saturation of remapped colors by
-	 * `gray`.
-	 */
-	void remapToGray(const uint8 color, const int8 gray);
-
-	/**
-	 * Configures a SingleRemap for the remap color `color`
-	 * to modify the brightness of remapped colors by
-	 * `percent`, and saturation of remapped colors by
-	 * `gray`.
-	 */
-	void remapToPercentGray(const uint8 color, const int16 gray, const int16 percent);
-
-	/**
-	 * Prevents GfxRemap32 from using the given range of
-	 * palette entries as potential remap targets.
-	 *
-	 * @NOTE Was DontMapToRange in SSCI.
-	 */
-	void blockRange(const uint8 from, const int16 count);
-
-	/**
-	 * Determines whether or not the given color has an
-	 * active remapper. If it does not, it is treated as a
-	 * skip color and the pixel is not drawn.
-	 *
-	 * @note SSCI uses a boolean array to decide whether a
-	 * a pixel is remapped, but it is possible to get the
-	 * same information from `_remaps`, as this function
-	 * does.
-	 * Presumably, the separate array was created for
-	 * performance reasons, since this is called a lot in
-	 * the most critical section of the renderer.
-	 */
-	inline bool remapEnabled(uint8 color) const {
-		const uint8 index = _remapEndColor - color;
-		assert(index < _remaps.size());
-		return (_remaps[index]._type != kRemapNone);
-	}
-
-	/**
-	 * Calculates the correct color for a target by looking
-	 * up the target color in the SingleRemap that controls
-	 * the given sourceColor. If there is no remap for the
-	 * given color, it will be treated as a skip color.
-	 */
-	inline uint8 remapColor(const uint8 sourceColor, const uint8 targetColor) const {
-		const uint8 index = _remapEndColor - sourceColor;
-		assert(index < _remaps.size());
-		const SingleRemap &singleRemap = _remaps[index];
-		assert(singleRemap._type != kRemapNone);
-		return singleRemap._remapColors[targetColor];
-	}
-
-	/**
-	 * Updates all active remaps in response to a palette
-	 * change or a remap settings change.
-	 *
-	 * `paletteChanged` is true if the next palette in
-	 * GfxPalette32 has been previously modified by other
-	 * palette operations.
-	 */
-	bool remapAllTables(const bool paletteUpdated);
-
-private:
-	typedef Common::Array<SingleRemap> SingleRemapsList;
-
-	/**
-	 * The first index of the remap area in the system
-	 * palette.
-	 */
-	const uint8 _remapStartColor;
-
-	/**
-	 * The last index of the remap area in the system
-	 * palette.
-	 */
-	uint8 _remapEndColor;
-
-	/**
-	 * The number of currently active remaps.
-	 */
-	uint8 _numActiveRemaps;
-
-	/**
-	 * The list of SingleRemaps.
-	 */
-	SingleRemapsList _remaps;
-
-	/**
-	 * If true, indicates that one or more SingleRemaps were
-	 * reconfigured and all remaps need to be recalculated.
-	 */
-	bool _needsUpdate;
-
-	/**
-	 * The first color that is blocked from being used as a
-	 * remap target color.
-	 */
-	uint8 _blockedRangeStart;
-
-	/**
-	 * The size of the range of blocked colors. If zero,
-	 * all colors are potential targets for remapping.
-	 */
-	int16 _blockedRangeCount;
-};
-} // End of namespace Sci
-#endif
diff --git a/engines/sci/graphics/view.h b/engines/sci/graphics/view.h
index 96b48c0..9159020 100644
--- a/engines/sci/graphics/view.h
+++ b/engines/sci/graphics/view.h
@@ -55,7 +55,6 @@ struct LoopInfo {
 
 class GfxScreen;
 class GfxPalette;
-class Resource;
 
 /**
  * View class, handles loading of view resources and drawing contained cels to screen
diff --git a/engines/sci/module.mk b/engines/sci/module.mk
index 5d54e2a..ba6c853 100644
--- a/engines/sci/module.mk
+++ b/engines/sci/module.mk
@@ -88,7 +88,6 @@ MODULE_OBJS += \
 	graphics/paint32.o \
 	graphics/plane32.o \
 	graphics/palette32.o \
-	graphics/remap32.o \
 	graphics/screen_item32.o \
 	graphics/text32.o \
 	sound/audio32.o \
diff --git a/engines/sci/sci.cpp b/engines/sci/sci.cpp
index 41fa144..b80456c 100644
--- a/engines/sci/sci.cpp
+++ b/engines/sci/sci.cpp
@@ -65,10 +65,9 @@
 
 #ifdef ENABLE_SCI32
 #include "sci/graphics/controls32.h"
-#include "sci/graphics/frameout.h"
 #include "sci/graphics/palette32.h"
-#include "sci/graphics/remap32.h"
 #include "sci/graphics/text32.h"
+#include "sci/graphics/frameout.h"
 #include "sci/sound/audio32.h"
 #include "sci/video/robot_decoder.h"
 #endif
@@ -700,7 +699,7 @@ void SciEngine::initGraphics() {
 	if (getSciVersion() >= SCI_VERSION_2) {
 		_gfxPalette32 = new GfxPalette32(_resMan, _gfxScreen);
 		_gfxPalette16 = _gfxPalette32;
-		_gfxRemap32 = new GfxRemap32();
+		_gfxRemap32 = new GfxRemap32(_gfxPalette32);
 	} else {
 #endif
 		_gfxPalette16 = new GfxPalette(_resMan, _gfxScreen);


Commit: 5a3d8337a68cd74277beac0ed0e51610723f6265
    https://github.com/scummvm/scummvm/commit/5a3d8337a68cd74277beac0ed0e51610723f6265
Author: Colin Snover (github.com at zetafleet.com)
Date: 2016-07-01T00:18:32+02:00

Commit Message:
SCI32: Change kRemapColors naming for consistency

Changed paths:
    engines/sci/engine/kernel.h
    engines/sci/engine/kernel_tables.h
    engines/sci/engine/kgraphics.cpp
    engines/sci/engine/kgraphics32.cpp



diff --git a/engines/sci/engine/kernel.h b/engines/sci/engine/kernel.h
index b269e97..1202982 100644
--- a/engines/sci/engine/kernel.h
+++ b/engines/sci/engine/kernel.h
@@ -412,7 +412,7 @@ reg_t kPlatform(EngineState *s, int argc, reg_t *argv);
 reg_t kTextColors(EngineState *s, int argc, reg_t *argv);
 reg_t kTextFonts(EngineState *s, int argc, reg_t *argv);
 reg_t kShow(EngineState *s, int argc, reg_t *argv);
-reg_t kRemapColors16(EngineState *s, int argc, reg_t *argv);
+reg_t kRemapColors(EngineState *s, int argc, reg_t *argv);
 reg_t kDummy(EngineState *s, int argc, reg_t *argv);
 reg_t kEmpty(EngineState *s, int argc, reg_t *argv);
 reg_t kStub(EngineState *s, int argc, reg_t *argv);
@@ -482,13 +482,13 @@ reg_t kScrollWindowDestroy(EngineState *s, int argc, reg_t *argv);
 
 reg_t kMulDiv(EngineState *s, int argc, reg_t *argv);
 
-reg_t kRemapColors(EngineState *s, int argc, reg_t *argv);
-reg_t kRemapOff(EngineState *s, int argc, reg_t *argv);
-reg_t kRemapByRange(EngineState *s, int argc, reg_t *argv);
-reg_t kRemapByPercent(EngineState *s, int argc, reg_t *argv);
-reg_t kRemapToGray(EngineState *s, int argc, reg_t *argv);
-reg_t kRemapToPercentGray(EngineState *s, int argc, reg_t *argv);
-reg_t kRemapSetNoMatchRange(EngineState *s, int argc, reg_t *argv);
+reg_t kRemapColors32(EngineState *s, int argc, reg_t *argv);
+reg_t kRemapColorsOff(EngineState *s, int argc, reg_t *argv);
+reg_t kRemapColorsByRange(EngineState *s, int argc, reg_t *argv);
+reg_t kRemapColorsByPercent(EngineState *s, int argc, reg_t *argv);
+reg_t kRemapColorsToGray(EngineState *s, int argc, reg_t *argv);
+reg_t kRemapColorsToPercentGray(EngineState *s, int argc, reg_t *argv);
+reg_t kRemapColorsBlockRange(EngineState *s, int argc, reg_t *argv);
 
 reg_t kAddScreenItem(EngineState *s, int argc, reg_t *argv);
 reg_t kUpdateScreenItem(EngineState *s, int argc, reg_t *argv);
diff --git a/engines/sci/engine/kernel_tables.h b/engines/sci/engine/kernel_tables.h
index 19bee35..dacaafe 100644
--- a/engines/sci/engine/kernel_tables.h
+++ b/engines/sci/engine/kernel_tables.h
@@ -414,12 +414,12 @@ static const SciKernelMapSubEntry kList_subops[] = {
 
 //    version,         subId, function-mapping,                    signature,              workarounds
 static const SciKernelMapSubEntry kRemapColors_subops[] = {
-	{ SIG_SCI32,           0, MAP_CALL(RemapOff),                 "(i)",                  NULL },
-	{ SIG_SCI32,           1, MAP_CALL(RemapByRange),             "iiii(i)",              NULL },
-	{ SIG_SCI32,           2, MAP_CALL(RemapByPercent),           "ii(i)",                NULL },
-	{ SIG_SCI32,           3, MAP_CALL(RemapToGray),              "ii(i)",                NULL },
-	{ SIG_SCI32,           4, MAP_CALL(RemapToPercentGray),       "iii(i)",               NULL },
-	{ SIG_SCI32,           5, MAP_CALL(RemapSetNoMatchRange),     "ii",                   NULL },
+	{ SIG_SCI32,           0, MAP_CALL(RemapColorsOff),            "(i)",                  NULL },
+	{ SIG_SCI32,           1, MAP_CALL(RemapColorsByRange),        "iiii(i)",              NULL },
+	{ SIG_SCI32,           2, MAP_CALL(RemapColorsByPercent),      "ii(i)",                NULL },
+	{ SIG_SCI32,           3, MAP_CALL(RemapColorsToGray),         "ii(i)",                NULL },
+	{ SIG_SCI32,           4, MAP_CALL(RemapColorsToPercentGray),  "iii(i)",               NULL },
+	{ SIG_SCI32,           5, MAP_CALL(RemapColorsBlockRange),     "ii",                   NULL },
 	SCI_SUBOPENTRY_TERMINATOR
 };
 
@@ -634,9 +634,9 @@ static SciKernelMapEntry s_kernelMap[] = {
 	{ MAP_CALL(PriCoord),          SIG_EVERYWHERE,           "i",                     NULL,            NULL },
 	{ MAP_CALL(Random),            SIG_EVERYWHERE,           "i(i)(i)",               NULL,            NULL },
 	{ MAP_CALL(ReadNumber),        SIG_EVERYWHERE,           "r",                     NULL,            kReadNumber_workarounds },
-	{ "RemapColors", kRemapColors16,       SIG_SCI11, SIGFOR_ALL,    "i(i)(i)(i)(i)", NULL,            NULL },
+	{ MAP_CALL(RemapColors),       SIG_SCI11, SIGFOR_ALL,    "i(i)(i)(i)(i)",         NULL,            NULL },
 #ifdef ENABLE_SCI32
-	{ MAP_CALL(RemapColors),       SIG_SCI32, SIGFOR_ALL,    "i(i)(i)(i)(i)(i)",      kRemapColors_subops, NULL },
+	{ "RemapColors", kRemapColors32, SIG_SCI32, SIGFOR_ALL,  "i(i)(i)(i)(i)(i)",      kRemapColors_subops, NULL },
 #endif
 	{ MAP_CALL(ResCheck),          SIG_EVERYWHERE,           "ii(iiii)",              NULL,            NULL },
 	{ MAP_CALL(RespondsTo),        SIG_EVERYWHERE,           ".i",                    NULL,            NULL },
diff --git a/engines/sci/engine/kgraphics.cpp b/engines/sci/engine/kgraphics.cpp
index 73236b9..cae5a09 100644
--- a/engines/sci/engine/kgraphics.cpp
+++ b/engines/sci/engine/kgraphics.cpp
@@ -1258,7 +1258,7 @@ reg_t kShow(EngineState *s, int argc, reg_t *argv) {
 }
 
 // Early variant of the SCI32 kRemapColors kernel function, used in the demo of QFG4
-reg_t kRemapColors16(EngineState *s, int argc, reg_t *argv) {
+reg_t kRemapColors(EngineState *s, int argc, reg_t *argv) {
 	uint16 operation = argv[0].toUint16();
 
 	switch (operation) {
diff --git a/engines/sci/engine/kgraphics32.cpp b/engines/sci/engine/kgraphics32.cpp
index 9270c81..80e8d4a 100644
--- a/engines/sci/engine/kgraphics32.cpp
+++ b/engines/sci/engine/kgraphics32.cpp
@@ -925,19 +925,19 @@ reg_t kPalCycle(EngineState *s, int argc, reg_t *argv) {
 	return s->r_acc;
 }
 
-reg_t kRemapColors(EngineState *s, int argc, reg_t *argv) {
+reg_t kRemapColors32(EngineState *s, int argc, reg_t *argv) {
 	if (!s)
 		return make_reg(0, getSciVersion());
 	error("not supposed to call this");
 }
 
-reg_t kRemapOff(EngineState *s, int argc, reg_t *argv) {
+reg_t kRemapColorsOff(EngineState *s, int argc, reg_t *argv) {
 	byte color = (argc >= 1) ? argv[0].toUint16() : 0;
 	g_sci->_gfxRemap32->remapOff(color);
 	return s->r_acc;
 }
 
-reg_t kRemapByRange(EngineState *s, int argc, reg_t *argv) {
+reg_t kRemapColorsByRange(EngineState *s, int argc, reg_t *argv) {
 	byte color = argv[0].toUint16();
 	byte from = argv[1].toUint16();
 	byte to = argv[2].toUint16();
@@ -947,7 +947,7 @@ reg_t kRemapByRange(EngineState *s, int argc, reg_t *argv) {
 	return s->r_acc;
 }
 
-reg_t kRemapByPercent(EngineState *s, int argc, reg_t *argv) {
+reg_t kRemapColorsByPercent(EngineState *s, int argc, reg_t *argv) {
 	byte color = argv[0].toUint16();
 	byte percent = argv[1].toUint16();
 	// The last parameter, depth, is unused
@@ -955,7 +955,7 @@ reg_t kRemapByPercent(EngineState *s, int argc, reg_t *argv) {
 	return s->r_acc;
 }
 
-reg_t kRemapToGray(EngineState *s, int argc, reg_t *argv) {
+reg_t kRemapColorsToGray(EngineState *s, int argc, reg_t *argv) {
 	byte color = argv[0].toUint16();
 	byte gray = argv[1].toUint16();
 	// The last parameter, depth, is unused
@@ -963,7 +963,7 @@ reg_t kRemapToGray(EngineState *s, int argc, reg_t *argv) {
 	return s->r_acc;
 }
 
-reg_t kRemapToPercentGray(EngineState *s, int argc, reg_t *argv) {
+reg_t kRemapColorsToPercentGray(EngineState *s, int argc, reg_t *argv) {
 	byte color = argv[0].toUint16();
 	byte gray = argv[1].toUint16();
 	byte percent = argv[2].toUint16();
@@ -972,7 +972,7 @@ reg_t kRemapToPercentGray(EngineState *s, int argc, reg_t *argv) {
 	return s->r_acc;
 }
 
-reg_t kRemapSetNoMatchRange(EngineState *s, int argc, reg_t *argv) {
+reg_t kRemapColorsBlockRange(EngineState *s, int argc, reg_t *argv) {
 	byte from = argv[0].toUint16();
 	byte count = argv[1].toUint16();
 	g_sci->_gfxRemap32->setNoMatchRange(from, count);


Commit: ed64d5b020e819b1b90cbd77682a8b36a6554950
    https://github.com/scummvm/scummvm/commit/ed64d5b020e819b1b90cbd77682a8b36a6554950
Author: Colin Snover (github.com at zetafleet.com)
Date: 2016-07-01T00:18:32+02:00

Commit Message:
SCI32: Move GfxRemap32 to separate files

Changed paths:
  A engines/sci/graphics/remap32.cpp
  A engines/sci/graphics/remap32.h
    engines/sci/engine/kgraphics32.cpp
    engines/sci/graphics/celobj32.cpp
    engines/sci/graphics/frameout.cpp
    engines/sci/graphics/palette32.cpp
    engines/sci/graphics/plane32.cpp
    engines/sci/graphics/remap.cpp
    engines/sci/graphics/remap.h
    engines/sci/module.mk
    engines/sci/sci.cpp



diff --git a/engines/sci/engine/kgraphics32.cpp b/engines/sci/engine/kgraphics32.cpp
index 80e8d4a..e85d2a9 100644
--- a/engines/sci/engine/kgraphics32.cpp
+++ b/engines/sci/engine/kgraphics32.cpp
@@ -54,6 +54,7 @@
 #include "sci/graphics/frameout.h"
 #include "sci/graphics/paint32.h"
 #include "sci/graphics/palette32.h"
+#include "sci/graphics/remap32.h"
 #include "sci/graphics/text32.h"
 #endif
 
diff --git a/engines/sci/graphics/celobj32.cpp b/engines/sci/graphics/celobj32.cpp
index 3018c6b..6559bba 100644
--- a/engines/sci/graphics/celobj32.cpp
+++ b/engines/sci/graphics/celobj32.cpp
@@ -26,10 +26,8 @@
 #include "sci/graphics/celobj32.h"
 #include "sci/graphics/frameout.h"
 #include "sci/graphics/palette32.h"
-#include "sci/graphics/picture.h"
-#include "sci/graphics/remap.h"
+#include "sci/graphics/remap32.h"
 #include "sci/graphics/text32.h"
-#include "sci/graphics/view.h"
 
 namespace Sci {
 #pragma mark CelScaler
diff --git a/engines/sci/graphics/frameout.cpp b/engines/sci/graphics/frameout.cpp
index 5771ab5..9d3ab04 100644
--- a/engines/sci/graphics/frameout.cpp
+++ b/engines/sci/graphics/frameout.cpp
@@ -42,15 +42,13 @@
 #include "sci/graphics/coordadjuster.h"
 #include "sci/graphics/compare.h"
 #include "sci/graphics/font.h"
-#include "sci/graphics/view.h"
 #include "sci/graphics/screen.h"
 #include "sci/graphics/paint32.h"
 #include "sci/graphics/palette32.h"
-#include "sci/graphics/picture.h"
-#include "sci/graphics/remap.h"
-#include "sci/graphics/text32.h"
 #include "sci/graphics/plane32.h"
+#include "sci/graphics/remap32.h"
 #include "sci/graphics/screen_item32.h"
+#include "sci/graphics/text32.h"
 #include "sci/graphics/frameout.h"
 #include "sci/video/robot_decoder.h"
 
diff --git a/engines/sci/graphics/palette32.cpp b/engines/sci/graphics/palette32.cpp
index 6844011..6c6234d 100644
--- a/engines/sci/graphics/palette32.cpp
+++ b/engines/sci/graphics/palette32.cpp
@@ -28,7 +28,7 @@
 #include "sci/event.h"
 #include "sci/resource.h"
 #include "sci/graphics/palette32.h"
-#include "sci/graphics/remap.h"
+#include "sci/graphics/remap32.h"
 #include "sci/graphics/screen.h"
 
 namespace Sci {
diff --git a/engines/sci/graphics/plane32.cpp b/engines/sci/graphics/plane32.cpp
index 6f5ad42..175875c 100644
--- a/engines/sci/graphics/plane32.cpp
+++ b/engines/sci/graphics/plane32.cpp
@@ -27,7 +27,7 @@
 #include "sci/graphics/frameout.h"
 #include "sci/graphics/lists32.h"
 #include "sci/graphics/plane32.h"
-#include "sci/graphics/remap.h"
+#include "sci/graphics/remap32.h"
 #include "sci/graphics/screen.h"
 #include "sci/graphics/screen_item32.h"
 
diff --git a/engines/sci/graphics/remap.cpp b/engines/sci/graphics/remap.cpp
index ff49e52..7f5721b 100644
--- a/engines/sci/graphics/remap.cpp
+++ b/engines/sci/graphics/remap.cpp
@@ -104,277 +104,4 @@ void GfxRemap::updateRemapping() {
 		}
 	}
 }
-
-#pragma mark -
-#pragma mark SCI32 remapping
-
-#ifdef ENABLE_SCI32
-
-GfxRemap32::GfxRemap32(GfxPalette32 *palette) : _palette(palette) {
-	for (int i = 0; i < REMAP_COLOR_COUNT; i++)
-		_remaps[i] = RemapParams(0, 0, 0, 0, 100, kRemappingNone);
-	_noMapStart = _noMapCount = 0;
-	_update = false;
-	_remapCount = 0;
-
-	// The remap range was 245 - 254 in SCI2, but was changed to 235 - 244 in SCI21 middle.
-	// All versions of KQ7 are using the older remap range semantics.
-	_remapEndColor = (getSciVersion() >= SCI_VERSION_2_1_MIDDLE || g_sci->getGameId() == GID_KQ7) ? 244 : 254;
-}
-
-void GfxRemap32::remapOff(byte color) {
-	if (!color) {
-		for (int i = 0; i < REMAP_COLOR_COUNT; i++)
-			_remaps[i] = RemapParams(0, 0, 0, 0, 100, kRemappingNone);
-
-		_remapCount = 0;
-	} else {
-		assert(_remapEndColor - color >= 0 && _remapEndColor - color < REMAP_COLOR_COUNT);
-		const byte index = _remapEndColor - color;
-		_remaps[index] = RemapParams(0, 0, 0, 0, 100, kRemappingNone);
-		_remapCount--;
-	}
-
-	_update = true;
-}
-
-void GfxRemap32::setRemappingRange(byte color, byte from, byte to, byte base) {
-	assert(_remapEndColor - color >= 0 && _remapEndColor - color < REMAP_COLOR_COUNT);
-	_remaps[_remapEndColor - color] = RemapParams(from, to, base, 0, 100, kRemappingByRange);
-	initColorArrays(_remapEndColor - color);
-	_remapCount++;
-	_update = true;
-}
-
-void GfxRemap32::setRemappingPercent(byte color, byte percent) {
-	assert(_remapEndColor - color >= 0 && _remapEndColor - color < REMAP_COLOR_COUNT);
-	_remaps[_remapEndColor - color] = RemapParams(0, 0, 0, 0, percent, kRemappingByPercent);
-	initColorArrays(_remapEndColor - color);
-	_remapCount++;
-	_update = true;
-}
-
-void GfxRemap32::setRemappingToGray(byte color, byte gray) {
-	assert(_remapEndColor - color >= 0 && _remapEndColor - color < REMAP_COLOR_COUNT);
-	_remaps[_remapEndColor - color] = RemapParams(0, 0, 0, gray, 100, kRemappingToGray);
-	initColorArrays(_remapEndColor - color);
-	_remapCount++;
-	_update = true;
-}
-
-void GfxRemap32::setRemappingToPercentGray(byte color, byte gray, byte percent) {
-	assert(_remapEndColor - color >= 0 && _remapEndColor - color < REMAP_COLOR_COUNT);
-	_remaps[_remapEndColor - color] = RemapParams(0, 0, 0, gray, percent, kRemappingToPercentGray);
-	initColorArrays(_remapEndColor - color);
-	_remapCount++;
-	_update = true;
-}
-
-void GfxRemap32::setNoMatchRange(byte from, byte count) {
-	_noMapStart = from;
-	_noMapCount = count;
-}
-
-bool GfxRemap32::remapEnabled(byte color) const {
-	assert(_remapEndColor - color >= 0 && _remapEndColor - color < REMAP_COLOR_COUNT);
-	const byte index = _remapEndColor - color;
-	return (_remaps[index].type != kRemappingNone);
-}
-
-byte GfxRemap32::remapColor(byte color, byte target) {
-	assert(_remapEndColor - color >= 0 && _remapEndColor - color < REMAP_COLOR_COUNT);
-	const byte index = _remapEndColor - color;
-	if (_remaps[index].type != kRemappingNone)
-		return _remaps[index].remap[target];
-	else
-		return target;
-}
-
-void GfxRemap32::initColorArrays(byte index) {
-	Palette *curPalette = &_palette->_sysPalette;
-	RemapParams *curRemap = &_remaps[index];
-
-	memcpy(curRemap->curColor, curPalette->colors, NON_REMAPPED_COLOR_COUNT * sizeof(Color));
-	memcpy(curRemap->targetColor, curPalette->colors, NON_REMAPPED_COLOR_COUNT * sizeof(Color));
-}
-
-bool GfxRemap32::updateRemap(byte index, bool palChanged) {
-	int result;
-	RemapParams *curRemap = &_remaps[index];
-	const Palette *curPalette = &_palette->_sysPalette;
-	const Palette *nextPalette = _palette->getNextPalette();
-	bool changed = false;
-
-	if (!_update && !palChanged)
-		return false;
-
-	Common::fill(_targetChanged, _targetChanged + NON_REMAPPED_COLOR_COUNT, false);
-
-	switch (curRemap->type) {
-	case kRemappingNone:
-		return false;
-	case kRemappingByRange:
-		for (int i = 0; i < NON_REMAPPED_COLOR_COUNT; i++)  {
-			if (curRemap->from <= i && i <= curRemap->to)
-				result = i + curRemap->base;
-			else
-				result = i;
-
-			if (curRemap->remap[i] != result) {
-				changed = true;
-				curRemap->remap[i] = result;
-			}
-
-			curRemap->colorChanged[i] = true;
-		}
-		return changed;
-	case kRemappingByPercent:
-		for (int i = 1; i < NON_REMAPPED_COLOR_COUNT; i++) {
-			// NOTE: This method uses nextPalette instead of curPalette
-			Color color = nextPalette->colors[i];
-
-			if (curRemap->curColor[i] != color) {
-				curRemap->colorChanged[i] = true;
-				curRemap->curColor[i] = color;
-			}
-
-			if (curRemap->percent != curRemap->oldPercent || curRemap->colorChanged[i])  {
-				byte red = CLIP<byte>(color.r * curRemap->percent / 100, 0, 255);
-				byte green = CLIP<byte>(color.g * curRemap->percent / 100, 0, 255);
-				byte blue = CLIP<byte>(color.b * curRemap->percent / 100, 0, 255);
-				byte used = curRemap->targetColor[i].used;
-
-				Color newColor = { used, red, green, blue };
-				if (curRemap->targetColor[i] != newColor)  {
-					_targetChanged[i] = true;
-					curRemap->targetColor[i] = newColor;
-				}
-			}
-		}
-		
-		changed = applyRemap(index);
-		Common::fill(curRemap->colorChanged, curRemap->colorChanged + NON_REMAPPED_COLOR_COUNT, false);
-		curRemap->oldPercent = curRemap->percent;
-		return changed;
-	case kRemappingToGray:
-		for (int i = 1; i < NON_REMAPPED_COLOR_COUNT; i++) {
-			Color color = curPalette->colors[i];
-
-			if (curRemap->curColor[i] != color) {
-				curRemap->colorChanged[i] = true;
-				curRemap->curColor[i] = color;
-			}
-
-			if (curRemap->gray != curRemap->oldGray || curRemap->colorChanged[i])  {
-				byte lumosity = ((color.r * 77) + (color.g * 151) + (color.b * 28)) >> 8;
-				byte red = CLIP<byte>(color.r - ((color.r - lumosity) * curRemap->gray / 100), 0, 255);
-				byte green = CLIP<byte>(color.g - ((color.g - lumosity) * curRemap->gray / 100), 0, 255);
-				byte blue = CLIP<byte>(color.b - ((color.b - lumosity) * curRemap->gray / 100), 0, 255);
-				byte used = curRemap->targetColor[i].used;
-
-				Color newColor = { used, red, green, blue };
-				if (curRemap->targetColor[i] != newColor)  {
-					_targetChanged[i] = true;
-					curRemap->targetColor[i] = newColor;
-				}
-			}
-		}
-
-		changed = applyRemap(index);
-		Common::fill(curRemap->colorChanged, curRemap->colorChanged + NON_REMAPPED_COLOR_COUNT, false);
-		curRemap->oldGray = curRemap->gray;
-		return changed;
-	case kRemappingToPercentGray:
-		for (int i = 1; i < NON_REMAPPED_COLOR_COUNT; i++) {
-			Color color = curPalette->colors[i];
-
-			if (curRemap->curColor[i] != color) {
-				curRemap->colorChanged[i] = true;
-				curRemap->curColor[i] = color;
-			}
-
-			if (curRemap->percent != curRemap->oldPercent || curRemap->gray != curRemap->oldGray || curRemap->colorChanged[i])  {
-				byte lumosity = ((color.r * 77) + (color.g * 151) + (color.b * 28)) >> 8;
-				lumosity = lumosity * curRemap->percent / 100;
-				byte red = CLIP<byte>(color.r - ((color.r - lumosity) * curRemap->gray / 100), 0, 255);
-				byte green = CLIP<byte>(color.g - ((color.g - lumosity) * curRemap->gray / 100), 0, 255);
-				byte blue = CLIP<byte>(color.b - ((color.b - lumosity) * curRemap->gray / 100), 0, 255);
-				byte used = curRemap->targetColor[i].used;
-
-				Color newColor = { used, red, green, blue };
-				if (curRemap->targetColor[i] != newColor)  {
-					_targetChanged[i] = true;
-					curRemap->targetColor[i] = newColor;
-				}
-			}
-		}
-
-		changed = applyRemap(index);
-		Common::fill(curRemap->colorChanged, curRemap->colorChanged + NON_REMAPPED_COLOR_COUNT, false);
-		curRemap->oldPercent = curRemap->percent;
-		curRemap->oldGray = curRemap->gray;
-		return changed;
-	default:
-		return false;
-	}
-}
-
-static int colorDistance(Color a, Color b) {
-	int rDiff = (a.r - b.r) * (a.r - b.r);
-	int gDiff = (a.g - b.g) * (a.g - b.g);
-	int bDiff = (a.b - b.b) * (a.b - b.b);
-	return rDiff + gDiff + bDiff;
-}
-
-bool GfxRemap32::applyRemap(byte index) {
-	RemapParams *curRemap = &_remaps[index];
-	const bool *cycleMap = _palette->getCyclemap();
-	bool unmappedColors[NON_REMAPPED_COLOR_COUNT];
-	bool changed = false;
-
-	Common::fill(unmappedColors, unmappedColors + NON_REMAPPED_COLOR_COUNT, false);
-	if (_noMapCount)
-		Common::fill(unmappedColors + _noMapStart, unmappedColors + _noMapStart + _noMapCount, true);
-
-	for (int i = 0; i < NON_REMAPPED_COLOR_COUNT; i++)  {
-		if (cycleMap[i])
-			unmappedColors[i] = true;
-	}
-
-	for (int i = 1; i < NON_REMAPPED_COLOR_COUNT; i++)  {
-		Color targetColor = curRemap->targetColor[i];
-		bool colorChanged = curRemap->colorChanged[curRemap->remap[i]];
-
-		if (!_targetChanged[i] && !colorChanged)
-			continue;
-
-		if (_targetChanged[i] && colorChanged)
-			if (curRemap->distance[i] < 100 && colorDistance(targetColor, curRemap->curColor[curRemap->remap[i]]) <= curRemap->distance[i])
-				continue;
-
-		int diff = 0;
-		int16 result = _palette->matchColor(targetColor.r, targetColor.g, targetColor.b, curRemap->distance[i], diff, unmappedColors);
-		if (result != -1 && curRemap->remap[i] != result)  {
-			changed = true;
-			curRemap->remap[i] = result;
-			curRemap->distance[i] = diff;
-		}
-	}
-
-	return changed;
-}
-
-bool GfxRemap32::remapAllTables(bool palChanged) {
-	bool changed = false;
-
-	for (int i = 0; i < REMAP_COLOR_COUNT; i++) {
-		changed |= updateRemap(i, palChanged);
-	}
-
-	_update = false;
-	return changed;
-}
-
-#endif
-
 } // End of namespace Sci
diff --git a/engines/sci/graphics/remap.h b/engines/sci/graphics/remap.h
index d012568..d758a97 100644
--- a/engines/sci/graphics/remap.h
+++ b/engines/sci/graphics/remap.h
@@ -38,9 +38,6 @@ enum ColorRemappingType {
 	kRemappingToPercentGray = 4
 };
 
-#define REMAP_COLOR_COUNT 9
-#define NON_REMAPPED_COLOR_COUNT 236
-
 /**
  * Remap class, handles color remapping
  */
@@ -68,87 +65,6 @@ private:
 	byte _remappingByRange[256];
 	uint16 _remappingPercentToSet;
 };
-
-#ifdef ENABLE_SCI32
-
-struct RemapParams {
-	byte from;
-	byte to;
-	byte base;
-	byte gray;
-	byte oldGray;
-	byte percent;
-	byte oldPercent;
-	ColorRemappingType type;
-	Color curColor[256];
-	Color targetColor[256];
-	byte distance[256];
-	byte remap[256];
-	bool colorChanged[256];
-
-	RemapParams() {
-		from = to = base = gray = oldGray = percent = oldPercent = 0;
-		type = kRemappingNone;
-
-		// curColor and targetColor are initialized in GfxRemap32::initColorArrays
-		memset(curColor, 0, 256 * sizeof(Color));
-		memset(targetColor, 0, 256 * sizeof(Color));
-		memset(distance, 0, 256);
-		for (int i = 0; i < NON_REMAPPED_COLOR_COUNT; i++)
-			remap[i] = i;
-		Common::fill(colorChanged, colorChanged + ARRAYSIZE(colorChanged), true);
-	}
-
-	RemapParams(byte from_, byte to_, byte base_, byte gray_, byte percent_, ColorRemappingType type_) {
-		from = from_;
-		to = to_;
-		base = base_;
-		gray = oldGray = gray_;
-		percent = oldPercent = percent_;
-		type = type_;
-
-		// curColor and targetColor are initialized in GfxRemap32::initColorArrays
-		memset(curColor, 0, 256 * sizeof(Color));
-		memset(targetColor, 0, 256 * sizeof(Color));
-		memset(distance, 0, 256);
-		for (int i = 0; i < NON_REMAPPED_COLOR_COUNT; i++)
-			remap[i] = i;
-		Common::fill(colorChanged, colorChanged + ARRAYSIZE(colorChanged), true);
-	}
-};
-
-class GfxRemap32 {
-public:
-	GfxRemap32(GfxPalette32 *palette);
-	~GfxRemap32() {}
-
-	void remapOff(byte color);
-	void setRemappingRange(byte color, byte from, byte to, byte base);
-	void setRemappingPercent(byte color, byte percent);
-	void setRemappingToGray(byte color, byte gray);
-	void setRemappingToPercentGray(byte color, byte gray, byte percent);
-	void setNoMatchRange(byte from, byte count);
-	bool remapEnabled(byte color) const;
-	byte remapColor(byte color, byte target);
-	bool remapAllTables(bool palChanged);
-	int getRemapCount() const { return _remapCount; }
-	int getStartColor() const { return _remapEndColor - REMAP_COLOR_COUNT + 1; }
-	int getEndColor() const { return _remapEndColor; }
-private:
-	GfxPalette32 *_palette;
-	RemapParams _remaps[REMAP_COLOR_COUNT];
-	bool _update;
-	byte _noMapStart, _noMapCount;
-	bool _targetChanged[NON_REMAPPED_COLOR_COUNT];
-	byte _remapEndColor;
-	int _remapCount;
-
-	void initColorArrays(byte index);
-	bool applyRemap(byte index);
-	bool updateRemap(byte index, bool palChanged);
-};
-#endif
-
 } // End of namespace Sci
 
 #endif
diff --git a/engines/sci/graphics/remap32.cpp b/engines/sci/graphics/remap32.cpp
new file mode 100644
index 0000000..5516d0e
--- /dev/null
+++ b/engines/sci/graphics/remap32.cpp
@@ -0,0 +1,294 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "sci/sci.h"
+#include "sci/graphics/palette32.h"
+#include "sci/graphics/remap32.h"
+
+namespace Sci {
+
+GfxRemap32::GfxRemap32(GfxPalette32 *palette) : _palette(palette) {
+	for (int i = 0; i < REMAP_COLOR_COUNT; i++)
+		_remaps[i] = RemapParams(0, 0, 0, 0, 100, kRemappingNone);
+	_noMapStart = _noMapCount = 0;
+	_update = false;
+	_remapCount = 0;
+
+	// The remap range was 245 - 254 in SCI2, but was changed to 235 - 244 in SCI21 middle.
+	// All versions of KQ7 are using the older remap range semantics.
+	_remapEndColor = (getSciVersion() >= SCI_VERSION_2_1_MIDDLE || g_sci->getGameId() == GID_KQ7) ? 244 : 254;
+}
+
+void GfxRemap32::remapOff(byte color) {
+	if (!color) {
+		for (int i = 0; i < REMAP_COLOR_COUNT; i++)
+			_remaps[i] = RemapParams(0, 0, 0, 0, 100, kRemappingNone);
+
+		_remapCount = 0;
+	} else {
+		assert(_remapEndColor - color >= 0 && _remapEndColor - color < REMAP_COLOR_COUNT);
+		const byte index = _remapEndColor - color;
+		_remaps[index] = RemapParams(0, 0, 0, 0, 100, kRemappingNone);
+		_remapCount--;
+	}
+
+	_update = true;
+}
+
+void GfxRemap32::setRemappingRange(byte color, byte from, byte to, byte base) {
+	assert(_remapEndColor - color >= 0 && _remapEndColor - color < REMAP_COLOR_COUNT);
+	_remaps[_remapEndColor - color] = RemapParams(from, to, base, 0, 100, kRemappingByRange);
+	initColorArrays(_remapEndColor - color);
+	_remapCount++;
+	_update = true;
+}
+
+void GfxRemap32::setRemappingPercent(byte color, byte percent) {
+	assert(_remapEndColor - color >= 0 && _remapEndColor - color < REMAP_COLOR_COUNT);
+	_remaps[_remapEndColor - color] = RemapParams(0, 0, 0, 0, percent, kRemappingByPercent);
+	initColorArrays(_remapEndColor - color);
+	_remapCount++;
+	_update = true;
+}
+
+void GfxRemap32::setRemappingToGray(byte color, byte gray) {
+	assert(_remapEndColor - color >= 0 && _remapEndColor - color < REMAP_COLOR_COUNT);
+	_remaps[_remapEndColor - color] = RemapParams(0, 0, 0, gray, 100, kRemappingToGray);
+	initColorArrays(_remapEndColor - color);
+	_remapCount++;
+	_update = true;
+}
+
+void GfxRemap32::setRemappingToPercentGray(byte color, byte gray, byte percent) {
+	assert(_remapEndColor - color >= 0 && _remapEndColor - color < REMAP_COLOR_COUNT);
+	_remaps[_remapEndColor - color] = RemapParams(0, 0, 0, gray, percent, kRemappingToPercentGray);
+	initColorArrays(_remapEndColor - color);
+	_remapCount++;
+	_update = true;
+}
+
+void GfxRemap32::setNoMatchRange(byte from, byte count) {
+	_noMapStart = from;
+	_noMapCount = count;
+}
+
+bool GfxRemap32::remapEnabled(byte color) const {
+	assert(_remapEndColor - color >= 0 && _remapEndColor - color < REMAP_COLOR_COUNT);
+	const byte index = _remapEndColor - color;
+	return (_remaps[index].type != kRemappingNone);
+}
+
+byte GfxRemap32::remapColor(byte color, byte target) {
+	assert(_remapEndColor - color >= 0 && _remapEndColor - color < REMAP_COLOR_COUNT);
+	const byte index = _remapEndColor - color;
+	if (_remaps[index].type != kRemappingNone)
+		return _remaps[index].remap[target];
+	else
+		return target;
+}
+
+void GfxRemap32::initColorArrays(byte index) {
+	Palette *curPalette = &_palette->_sysPalette;
+	RemapParams *curRemap = &_remaps[index];
+
+	memcpy(curRemap->curColor, curPalette->colors, NON_REMAPPED_COLOR_COUNT * sizeof(Color));
+	memcpy(curRemap->targetColor, curPalette->colors, NON_REMAPPED_COLOR_COUNT * sizeof(Color));
+}
+
+bool GfxRemap32::updateRemap(byte index, bool palChanged) {
+	int result;
+	RemapParams *curRemap = &_remaps[index];
+	const Palette *curPalette = &_palette->_sysPalette;
+	const Palette *nextPalette = _palette->getNextPalette();
+	bool changed = false;
+
+	if (!_update && !palChanged)
+		return false;
+
+	Common::fill(_targetChanged, _targetChanged + NON_REMAPPED_COLOR_COUNT, false);
+
+	switch (curRemap->type) {
+	case kRemappingNone:
+		return false;
+	case kRemappingByRange:
+		for (int i = 0; i < NON_REMAPPED_COLOR_COUNT; i++)  {
+			if (curRemap->from <= i && i <= curRemap->to)
+				result = i + curRemap->base;
+			else
+				result = i;
+
+			if (curRemap->remap[i] != result) {
+				changed = true;
+				curRemap->remap[i] = result;
+			}
+
+			curRemap->colorChanged[i] = true;
+		}
+		return changed;
+	case kRemappingByPercent:
+		for (int i = 1; i < NON_REMAPPED_COLOR_COUNT; i++) {
+			// NOTE: This method uses nextPalette instead of curPalette
+			Color color = nextPalette->colors[i];
+
+			if (curRemap->curColor[i] != color) {
+				curRemap->colorChanged[i] = true;
+				curRemap->curColor[i] = color;
+			}
+
+			if (curRemap->percent != curRemap->oldPercent || curRemap->colorChanged[i])  {
+				byte red = CLIP<byte>(color.r * curRemap->percent / 100, 0, 255);
+				byte green = CLIP<byte>(color.g * curRemap->percent / 100, 0, 255);
+				byte blue = CLIP<byte>(color.b * curRemap->percent / 100, 0, 255);
+				byte used = curRemap->targetColor[i].used;
+
+				Color newColor = { used, red, green, blue };
+				if (curRemap->targetColor[i] != newColor)  {
+					_targetChanged[i] = true;
+					curRemap->targetColor[i] = newColor;
+				}
+			}
+		}
+		
+		changed = applyRemap(index);
+		Common::fill(curRemap->colorChanged, curRemap->colorChanged + NON_REMAPPED_COLOR_COUNT, false);
+		curRemap->oldPercent = curRemap->percent;
+		return changed;
+	case kRemappingToGray:
+		for (int i = 1; i < NON_REMAPPED_COLOR_COUNT; i++) {
+			Color color = curPalette->colors[i];
+
+			if (curRemap->curColor[i] != color) {
+				curRemap->colorChanged[i] = true;
+				curRemap->curColor[i] = color;
+			}
+
+			if (curRemap->gray != curRemap->oldGray || curRemap->colorChanged[i])  {
+				byte lumosity = ((color.r * 77) + (color.g * 151) + (color.b * 28)) >> 8;
+				byte red = CLIP<byte>(color.r - ((color.r - lumosity) * curRemap->gray / 100), 0, 255);
+				byte green = CLIP<byte>(color.g - ((color.g - lumosity) * curRemap->gray / 100), 0, 255);
+				byte blue = CLIP<byte>(color.b - ((color.b - lumosity) * curRemap->gray / 100), 0, 255);
+				byte used = curRemap->targetColor[i].used;
+
+				Color newColor = { used, red, green, blue };
+				if (curRemap->targetColor[i] != newColor)  {
+					_targetChanged[i] = true;
+					curRemap->targetColor[i] = newColor;
+				}
+			}
+		}
+
+		changed = applyRemap(index);
+		Common::fill(curRemap->colorChanged, curRemap->colorChanged + NON_REMAPPED_COLOR_COUNT, false);
+		curRemap->oldGray = curRemap->gray;
+		return changed;
+	case kRemappingToPercentGray:
+		for (int i = 1; i < NON_REMAPPED_COLOR_COUNT; i++) {
+			Color color = curPalette->colors[i];
+
+			if (curRemap->curColor[i] != color) {
+				curRemap->colorChanged[i] = true;
+				curRemap->curColor[i] = color;
+			}
+
+			if (curRemap->percent != curRemap->oldPercent || curRemap->gray != curRemap->oldGray || curRemap->colorChanged[i])  {
+				byte lumosity = ((color.r * 77) + (color.g * 151) + (color.b * 28)) >> 8;
+				lumosity = lumosity * curRemap->percent / 100;
+				byte red = CLIP<byte>(color.r - ((color.r - lumosity) * curRemap->gray / 100), 0, 255);
+				byte green = CLIP<byte>(color.g - ((color.g - lumosity) * curRemap->gray / 100), 0, 255);
+				byte blue = CLIP<byte>(color.b - ((color.b - lumosity) * curRemap->gray / 100), 0, 255);
+				byte used = curRemap->targetColor[i].used;
+
+				Color newColor = { used, red, green, blue };
+				if (curRemap->targetColor[i] != newColor)  {
+					_targetChanged[i] = true;
+					curRemap->targetColor[i] = newColor;
+				}
+			}
+		}
+
+		changed = applyRemap(index);
+		Common::fill(curRemap->colorChanged, curRemap->colorChanged + NON_REMAPPED_COLOR_COUNT, false);
+		curRemap->oldPercent = curRemap->percent;
+		curRemap->oldGray = curRemap->gray;
+		return changed;
+	default:
+		return false;
+	}
+}
+
+static int colorDistance(Color a, Color b) {
+	int rDiff = (a.r - b.r) * (a.r - b.r);
+	int gDiff = (a.g - b.g) * (a.g - b.g);
+	int bDiff = (a.b - b.b) * (a.b - b.b);
+	return rDiff + gDiff + bDiff;
+}
+
+bool GfxRemap32::applyRemap(byte index) {
+	RemapParams *curRemap = &_remaps[index];
+	const bool *cycleMap = _palette->getCyclemap();
+	bool unmappedColors[NON_REMAPPED_COLOR_COUNT];
+	bool changed = false;
+
+	Common::fill(unmappedColors, unmappedColors + NON_REMAPPED_COLOR_COUNT, false);
+	if (_noMapCount)
+		Common::fill(unmappedColors + _noMapStart, unmappedColors + _noMapStart + _noMapCount, true);
+
+	for (int i = 0; i < NON_REMAPPED_COLOR_COUNT; i++)  {
+		if (cycleMap[i])
+			unmappedColors[i] = true;
+	}
+
+	for (int i = 1; i < NON_REMAPPED_COLOR_COUNT; i++)  {
+		Color targetColor = curRemap->targetColor[i];
+		bool colorChanged = curRemap->colorChanged[curRemap->remap[i]];
+
+		if (!_targetChanged[i] && !colorChanged)
+			continue;
+
+		if (_targetChanged[i] && colorChanged)
+			if (curRemap->distance[i] < 100 && colorDistance(targetColor, curRemap->curColor[curRemap->remap[i]]) <= curRemap->distance[i])
+				continue;
+
+		int diff = 0;
+		int16 result = _palette->matchColor(targetColor.r, targetColor.g, targetColor.b, curRemap->distance[i], diff, unmappedColors);
+		if (result != -1 && curRemap->remap[i] != result)  {
+			changed = true;
+			curRemap->remap[i] = result;
+			curRemap->distance[i] = diff;
+		}
+	}
+
+	return changed;
+}
+
+bool GfxRemap32::remapAllTables(bool palChanged) {
+	bool changed = false;
+
+	for (int i = 0; i < REMAP_COLOR_COUNT; i++) {
+		changed |= updateRemap(i, palChanged);
+	}
+
+	_update = false;
+	return changed;
+}
+
+} // End of namespace Sci
diff --git a/engines/sci/graphics/remap32.h b/engines/sci/graphics/remap32.h
new file mode 100644
index 0000000..ff871f6
--- /dev/null
+++ b/engines/sci/graphics/remap32.h
@@ -0,0 +1,113 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef SCI_GRAPHICS_REMAP32_H
+#define SCI_GRAPHICS_REMAP32_H
+
+#include "common/array.h"
+#include "sci/graphics/remap.h"
+
+namespace Sci {
+
+#define REMAP_COLOR_COUNT 9
+#define NON_REMAPPED_COLOR_COUNT 236
+
+struct RemapParams {
+	byte from;
+	byte to;
+	byte base;
+	byte gray;
+	byte oldGray;
+	byte percent;
+	byte oldPercent;
+	ColorRemappingType type;
+	Color curColor[256];
+	Color targetColor[256];
+	byte distance[256];
+	byte remap[256];
+	bool colorChanged[256];
+
+	RemapParams() {
+		from = to = base = gray = oldGray = percent = oldPercent = 0;
+		type = kRemappingNone;
+
+		// curColor and targetColor are initialized in GfxRemap32::initColorArrays
+		memset(curColor, 0, 256 * sizeof(Color));
+		memset(targetColor, 0, 256 * sizeof(Color));
+		memset(distance, 0, 256);
+		for (int i = 0; i < NON_REMAPPED_COLOR_COUNT; i++)
+			remap[i] = i;
+		Common::fill(colorChanged, colorChanged + ARRAYSIZE(colorChanged), true);
+	}
+
+	RemapParams(byte from_, byte to_, byte base_, byte gray_, byte percent_, ColorRemappingType type_) {
+		from = from_;
+		to = to_;
+		base = base_;
+		gray = oldGray = gray_;
+		percent = oldPercent = percent_;
+		type = type_;
+
+		// curColor and targetColor are initialized in GfxRemap32::initColorArrays
+		memset(curColor, 0, 256 * sizeof(Color));
+		memset(targetColor, 0, 256 * sizeof(Color));
+		memset(distance, 0, 256);
+		for (int i = 0; i < NON_REMAPPED_COLOR_COUNT; i++)
+			remap[i] = i;
+		Common::fill(colorChanged, colorChanged + ARRAYSIZE(colorChanged), true);
+	}
+};
+
+class GfxRemap32 {
+public:
+	GfxRemap32(GfxPalette32 *palette);
+	~GfxRemap32() {}
+
+	void remapOff(byte color);
+	void setRemappingRange(byte color, byte from, byte to, byte base);
+	void setRemappingPercent(byte color, byte percent);
+	void setRemappingToGray(byte color, byte gray);
+	void setRemappingToPercentGray(byte color, byte gray, byte percent);
+	void setNoMatchRange(byte from, byte count);
+	bool remapEnabled(byte color) const;
+	byte remapColor(byte color, byte target);
+	bool remapAllTables(bool palChanged);
+	int getRemapCount() const { return _remapCount; }
+	int getStartColor() const { return _remapEndColor - REMAP_COLOR_COUNT + 1; }
+	int getEndColor() const { return _remapEndColor; }
+private:
+	GfxPalette32 *_palette;
+	RemapParams _remaps[REMAP_COLOR_COUNT];
+	bool _update;
+	byte _noMapStart, _noMapCount;
+	bool _targetChanged[NON_REMAPPED_COLOR_COUNT];
+	byte _remapEndColor;
+	int _remapCount;
+
+	void initColorArrays(byte index);
+	bool applyRemap(byte index);
+	bool updateRemap(byte index, bool palChanged);
+};
+
+} // End of namespace Sci
+
+#endif
diff --git a/engines/sci/module.mk b/engines/sci/module.mk
index ba6c853..5d54e2a 100644
--- a/engines/sci/module.mk
+++ b/engines/sci/module.mk
@@ -88,6 +88,7 @@ MODULE_OBJS += \
 	graphics/paint32.o \
 	graphics/plane32.o \
 	graphics/palette32.o \
+	graphics/remap32.o \
 	graphics/screen_item32.o \
 	graphics/text32.o \
 	sound/audio32.o \
diff --git a/engines/sci/sci.cpp b/engines/sci/sci.cpp
index b80456c..243c12b 100644
--- a/engines/sci/sci.cpp
+++ b/engines/sci/sci.cpp
@@ -65,9 +65,10 @@
 
 #ifdef ENABLE_SCI32
 #include "sci/graphics/controls32.h"
+#include "sci/graphics/frameout.h"
 #include "sci/graphics/palette32.h"
+#include "sci/graphics/remap32.h"
 #include "sci/graphics/text32.h"
-#include "sci/graphics/frameout.h"
 #include "sci/sound/audio32.h"
 #include "sci/video/robot_decoder.h"
 #endif


Commit: 14e57ac867076e754f5299ff1418b3f184a9bf67
    https://github.com/scummvm/scummvm/commit/14e57ac867076e754f5299ff1418b3f184a9bf67
Author: Colin Snover (github.com at zetafleet.com)
Date: 2016-07-01T00:18:32+02:00

Commit Message:
SCI32: Add missing remap CelObj calls

Changed paths:
    engines/sci/graphics/celobj32.cpp



diff --git a/engines/sci/graphics/celobj32.cpp b/engines/sci/graphics/celobj32.cpp
index 6559bba..befa5cd 100644
--- a/engines/sci/graphics/celobj32.cpp
+++ b/engines/sci/graphics/celobj32.cpp
@@ -31,6 +31,7 @@
 
 namespace Sci {
 #pragma mark CelScaler
+
 CelScaler *CelObj::_scaler = nullptr;
 
 void CelScaler::activateScaleTables(const Ratio &scaleX, const Ratio &scaleY) {
@@ -321,6 +322,10 @@ public:
 #pragma mark -
 #pragma mark CelObj - Remappers
 
+/**
+ * Pixel mapper for a CelObj with transparent pixels and no
+ * remapping data.
+ */
 struct MAPPER_NoMD {
 	inline void draw(byte *target, const byte pixel, const uint8 skipColor) const {
 		if (pixel != skipColor) {
@@ -328,25 +333,49 @@ struct MAPPER_NoMD {
 		}
 	}
 };
+
+/**
+ * Pixel mapper for a CelObj with no transparent pixels and
+ * no remapping data.
+ */
 struct MAPPER_NoMDNoSkip {
 	inline void draw(byte *target, const byte pixel, const uint8) const {
 		*target = pixel;
 	}
 };
 
+/**
+ * Pixel mapper for a CelObj with transparent pixels,
+ * remapping data, and remapping enabled.
+ */
 struct MAPPER_Map {
 	inline void draw(byte *target, const byte pixel, const uint8 skipColor) const {
 		if (pixel != skipColor) {
+			// NOTE: For some reason, SSCI never checks if the source
+			// pixel is *above* the range of remaps.
 			if (pixel < g_sci->_gfxRemap32->getStartColor()) {
 				*target = pixel;
-			} else {
-				if (g_sci->_gfxRemap32->remapEnabled(pixel))
-					*target = g_sci->_gfxRemap32->remapColor(pixel, *target);
+			} else if (g_sci->_gfxRemap32->remapEnabled(pixel)) {
+				*target = g_sci->_gfxRemap32->remapColor(pixel, *target);
 			}
 		}
 	}
 };
 
+/**
+ * Pixel mapper for a CelObj with transparent pixels,
+ * remapping data, and remapping disabled.
+ */
+struct MAPPER_NoMap {
+	inline void draw(byte *target, const byte pixel, const uint8 skipColor) const {
+		// NOTE: For some reason, SSCI never checks if the source
+		// pixel is *above* the range of remaps.
+		if (pixel != skipColor && pixel < g_sci->_gfxRemap32->getStartColor()) {
+			*target = pixel;
+		}
+	}
+};
+
 void CelObj::draw(Buffer &target, const ScreenItem &screenItem, const Common::Rect &targetRect) const {
 	const Common::Point &scaledPosition = screenItem._scaledPosition;
 	const Ratio &scaleX = screenItem._ratioX;
@@ -521,6 +550,7 @@ void CelObj::submitPalette() const {
 
 #pragma mark -
 #pragma mark CelObj - Caching
+
 int CelObj::_nextCacheId = 1;
 CelCache *CelObj::_cache = nullptr;
 
@@ -622,33 +652,35 @@ void dummyFill(Buffer &target, const Common::Rect &targetRect) {
 }
 
 void CelObj::drawHzFlip(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const {
-	debug("drawHzFlip");
-	dummyFill(target, targetRect);
+	render<MAPPER_NoMap, SCALER_NoScale<true, READER_Compressed> >(target, targetRect, scaledPosition);
 }
 
 void CelObj::drawNoFlip(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const {
-	debug("drawNoFlip");
-	dummyFill(target, targetRect);
+	render<MAPPER_NoMap, SCALER_NoScale<false, READER_Compressed> >(target, targetRect, scaledPosition);
 }
 
 void CelObj::drawUncompNoFlip(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const {
-	debug("drawUncompNoFlip");
-	dummyFill(target, targetRect);
+	render<MAPPER_NoMap, SCALER_NoScale<false, READER_Uncompressed> >(target, targetRect, scaledPosition);
 }
 
 void CelObj::drawUncompHzFlip(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const {
-	debug("drawUncompHzFlip");
-	dummyFill(target, targetRect);
+	render<MAPPER_NoMap, SCALER_NoScale<true, READER_Uncompressed> >(target, targetRect, scaledPosition);
 }
 
 void CelObj::scaleDraw(Buffer &target, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition) const {
-	debug("scaleDraw");
-	dummyFill(target, targetRect);
+	if (_drawMirrored) {
+		render<MAPPER_NoMap, SCALER_Scale<true, READER_Compressed> >(target, targetRect, scaledPosition, scaleX, scaleY);
+	} else {
+		render<MAPPER_NoMap, SCALER_Scale<false, READER_Compressed> >(target, targetRect, scaledPosition, scaleX, scaleY);
+	}
 }
 
 void CelObj::scaleDrawUncomp(Buffer &target, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition) const {
-	debug("scaleDrawUncomp");
-	dummyFill(target, targetRect);
+	if (_drawMirrored) {
+		render<MAPPER_NoMap, SCALER_Scale<true, READER_Uncompressed> >(target, targetRect, scaledPosition, scaleX, scaleY);
+	} else {
+		render<MAPPER_NoMap, SCALER_Scale<false, READER_Uncompressed> >(target, targetRect, scaledPosition, scaleX, scaleY);
+	}
 }
 
 void CelObj::drawHzFlipMap(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const {
@@ -668,17 +700,19 @@ void CelObj::drawUncompHzFlipMap(Buffer &target, const Common::Rect &targetRect,
 }
 
 void CelObj::scaleDrawMap(Buffer &target, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition) const {
-	if (_drawMirrored)
+	if (_drawMirrored) {
 		render<MAPPER_Map, SCALER_Scale<true, READER_Compressed> >(target, targetRect, scaledPosition, scaleX, scaleY);
-	else
+	} else {
 		render<MAPPER_Map, SCALER_Scale<false, READER_Compressed> >(target, targetRect, scaledPosition, scaleX, scaleY);
+	}
 }
 
 void CelObj::scaleDrawUncompMap(Buffer &target, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition) const {
-	if (_drawMirrored)
+	if (_drawMirrored) {
 		render<MAPPER_Map, SCALER_Scale<true, READER_Uncompressed> >(target, targetRect, scaledPosition, scaleX, scaleY);
-	else
+	} else {
 		render<MAPPER_Map, SCALER_Scale<false, READER_Uncompressed> >(target, targetRect, scaledPosition, scaleX, scaleY);
+	}
 }
 
 void CelObj::drawNoFlipNoMD(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const {
@@ -713,14 +747,16 @@ void CelObj::scaleDrawNoMD(Buffer &target, const Ratio &scaleX, const Ratio &sca
 }
 
 void CelObj::scaleDrawUncompNoMD(Buffer &target, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition) const {
-	if (_drawMirrored)
+	if (_drawMirrored) {
 		render<MAPPER_NoMD, SCALER_Scale<true, READER_Uncompressed> >(target, targetRect, scaledPosition, scaleX, scaleY);
-	else
+	} else {
 		render<MAPPER_NoMD, SCALER_Scale<false, READER_Uncompressed> >(target, targetRect, scaledPosition, scaleX, scaleY);
+	}
 }
 
 #pragma mark -
 #pragma mark CelObjView
+
 CelObjView::CelObjView(const GuiResourceId viewId, const int16 loopNo, const int16 celNo) {
 	_info.type = kCelTypeView;
 	_info.resourceId = viewId;
@@ -838,8 +874,12 @@ CelObjView::CelObjView(const GuiResourceId viewId, const int16 loopNo, const int
 bool CelObjView::analyzeUncompressedForRemap() const {
 	byte *pixels = getResPointer() + READ_SCI11ENDIAN_UINT32(getResPointer() + _celHeaderOffset + 24);
 	for (int i = 0; i < _width * _height; ++i) {
-		byte pixel = pixels[i];
-		if (pixel >= g_sci->_gfxRemap32->getStartColor() && pixel <= g_sci->_gfxRemap32->getEndColor() && pixel != _transparentColor) {
+		const byte pixel = pixels[i];
+		if (
+			pixel >= g_sci->_gfxRemap32->getStartColor() &&
+			pixel <= g_sci->_gfxRemap32->getEndColor() &&
+			pixel != _transparentColor
+		) {
 			return true;
 		}
 	}
@@ -851,8 +891,12 @@ bool CelObjView::analyzeForRemap() const {
 	for (int y = 0; y < _height; y++) {
 		const byte *curRow = reader.getRow(y);
 		for (int x = 0; x < _width; x++) {
-			byte pixel = curRow[x];
-			if (pixel >= g_sci->_gfxRemap32->getStartColor() && pixel <= g_sci->_gfxRemap32->getEndColor() && pixel != _transparentColor) {
+			const byte pixel = curRow[x];
+			if (
+				pixel >= g_sci->_gfxRemap32->getStartColor() &&
+				pixel <= g_sci->_gfxRemap32->getEndColor() &&
+				pixel != _transparentColor
+			) {
 				return true;
 			}
 		}
@@ -879,6 +923,7 @@ byte *CelObjView::getResPointer() const {
 
 #pragma mark -
 #pragma mark CelObjPic
+
 CelObjPic::CelObjPic(const GuiResourceId picId, const int16 celNo) {
 	_info.type = kCelTypePic;
 	_info.resourceId = picId;
@@ -1000,6 +1045,7 @@ byte *CelObjPic::getResPointer() const {
 
 #pragma mark -
 #pragma mark CelObjMem
+
 CelObjMem::CelObjMem(const reg_t bitmapObject) {
 	_info.type = kCelTypeMem;
 	_info.bitmap = bitmapObject;
@@ -1029,6 +1075,7 @@ byte *CelObjMem::getResPointer() const {
 
 #pragma mark -
 #pragma mark CelObjColor
+
 CelObjColor::CelObjColor(const uint8 color, const int16 width, const int16 height) {
 	_info.type = kCelTypeColor;
 	_info.color = color;


Commit: 0aed08681eff9589dd7747b0285b07ef5a7cc155
    https://github.com/scummvm/scummvm/commit/0aed08681eff9589dd7747b0285b07ef5a7cc155
Author: Colin Snover (github.com at zetafleet.com)
Date: 2016-07-01T00:18:32+02:00

Commit Message:
SCI32: Separate remap types

Changed paths:
    engines/sci/graphics/remap.cpp
    engines/sci/graphics/remap.h
    engines/sci/graphics/remap32.cpp
    engines/sci/graphics/remap32.h
    engines/sci/graphics/view.h



diff --git a/engines/sci/graphics/remap.cpp b/engines/sci/graphics/remap.cpp
index 7f5721b..2abf03e 100644
--- a/engines/sci/graphics/remap.cpp
+++ b/engines/sci/graphics/remap.cpp
@@ -21,31 +21,23 @@
  */
 
 #include "sci/sci.h"
-#include "sci/resource.h"
 #include "sci/graphics/palette.h"
-#include "sci/graphics/palette32.h"
 #include "sci/graphics/remap.h"
 #include "sci/graphics/screen.h"
 
 namespace Sci {
 
-#pragma mark -
-#pragma mark SCI16 remapping (QFG4 demo)
-
 GfxRemap::GfxRemap(GfxPalette *palette)
 	: _palette(palette) {
 	_remapOn = false;
 	resetRemapping();
 }
 
-GfxRemap::~GfxRemap() {
-}
-
 byte GfxRemap::remapColor(byte remappedColor, byte screenColor) {
 	assert(_remapOn);
-	if (_remappingType[remappedColor] == kRemappingByRange)
+	if (_remappingType[remappedColor] == kRemapByRange)
 		return _remappingByRange[screenColor];
-	else if (_remappingType[remappedColor] == kRemappingByPercent)
+	else if (_remappingType[remappedColor] == kRemapByPercent)
 		return _remappingByPercent[screenColor];
 	else
 		error("remapColor(): Color %d isn't remapped", remappedColor);
@@ -58,7 +50,7 @@ void GfxRemap::resetRemapping() {
 	_remappingPercentToSet = 0;
 
 	for (int i = 0; i < 256; i++) {
-		_remappingType[i] = kRemappingNone;
+		_remappingType[i] = kRemapNone;
 		_remappingByPercent[i] = i;
 		_remappingByRange[i] = i;
 	}
@@ -80,7 +72,7 @@ void GfxRemap::setRemappingPercent(byte color, byte percent) {
 		_remappingByPercent[i] = _palette->kernelFindColor(r, g, b);
 	}
 
-	_remappingType[color] = kRemappingByPercent;
+	_remappingType[color] = kRemapByPercent;
 }
 
 void GfxRemap::setRemappingRange(byte color, byte from, byte to, byte base) {
@@ -90,7 +82,7 @@ void GfxRemap::setRemappingRange(byte color, byte from, byte to, byte base) {
 		_remappingByRange[i] = i + base;
 	}
 
-	_remappingType[color] = kRemappingByRange;
+	_remappingType[color] = kRemapByRange;
 }
 
 void GfxRemap::updateRemapping() {
diff --git a/engines/sci/graphics/remap.h b/engines/sci/graphics/remap.h
index d758a97..a9cd76a 100644
--- a/engines/sci/graphics/remap.h
+++ b/engines/sci/graphics/remap.h
@@ -30,33 +30,30 @@ namespace Sci {
 
 class GfxScreen;
 
-enum ColorRemappingType {
-	kRemappingNone = 0,
-	kRemappingByRange = 1,
-	kRemappingByPercent = 2,
-	kRemappingToGray = 3,
-	kRemappingToPercentGray = 4
-};
-
 /**
- * Remap class, handles color remapping
+ * This class handles color remapping for the QFG4 demo.
  */
 class GfxRemap {
+private:
+	enum ColorRemappingType {
+		kRemapNone = 0,
+		kRemapByRange = 1,
+		kRemapByPercent = 2
+	};
+
 public:
 	GfxRemap(GfxPalette *_palette);
-	~GfxRemap();
 
 	void resetRemapping();
 	void setRemappingPercent(byte color, byte percent);
 	void setRemappingRange(byte color, byte from, byte to, byte base);
 	bool isRemapped(byte color) const {
-		return _remapOn && (_remappingType[color] != kRemappingNone);
+		return _remapOn && (_remappingType[color] != kRemapNone);
 	}
 	byte remapColor(byte remappedColor, byte screenColor);
 	void updateRemapping();
 
 private:
-	GfxScreen *_screen;
 	GfxPalette *_palette;
 
 	bool _remapOn;
diff --git a/engines/sci/graphics/remap32.cpp b/engines/sci/graphics/remap32.cpp
index 5516d0e..774c4ca 100644
--- a/engines/sci/graphics/remap32.cpp
+++ b/engines/sci/graphics/remap32.cpp
@@ -28,7 +28,7 @@ namespace Sci {
 
 GfxRemap32::GfxRemap32(GfxPalette32 *palette) : _palette(palette) {
 	for (int i = 0; i < REMAP_COLOR_COUNT; i++)
-		_remaps[i] = RemapParams(0, 0, 0, 0, 100, kRemappingNone);
+		_remaps[i] = RemapParams(0, 0, 0, 0, 100, kRemapNone);
 	_noMapStart = _noMapCount = 0;
 	_update = false;
 	_remapCount = 0;
@@ -41,13 +41,13 @@ GfxRemap32::GfxRemap32(GfxPalette32 *palette) : _palette(palette) {
 void GfxRemap32::remapOff(byte color) {
 	if (!color) {
 		for (int i = 0; i < REMAP_COLOR_COUNT; i++)
-			_remaps[i] = RemapParams(0, 0, 0, 0, 100, kRemappingNone);
+			_remaps[i] = RemapParams(0, 0, 0, 0, 100, kRemapNone);
 
 		_remapCount = 0;
 	} else {
 		assert(_remapEndColor - color >= 0 && _remapEndColor - color < REMAP_COLOR_COUNT);
 		const byte index = _remapEndColor - color;
-		_remaps[index] = RemapParams(0, 0, 0, 0, 100, kRemappingNone);
+		_remaps[index] = RemapParams(0, 0, 0, 0, 100, kRemapNone);
 		_remapCount--;
 	}
 
@@ -56,7 +56,7 @@ void GfxRemap32::remapOff(byte color) {
 
 void GfxRemap32::setRemappingRange(byte color, byte from, byte to, byte base) {
 	assert(_remapEndColor - color >= 0 && _remapEndColor - color < REMAP_COLOR_COUNT);
-	_remaps[_remapEndColor - color] = RemapParams(from, to, base, 0, 100, kRemappingByRange);
+	_remaps[_remapEndColor - color] = RemapParams(from, to, base, 0, 100, kRemapByRange);
 	initColorArrays(_remapEndColor - color);
 	_remapCount++;
 	_update = true;
@@ -64,7 +64,7 @@ void GfxRemap32::setRemappingRange(byte color, byte from, byte to, byte base) {
 
 void GfxRemap32::setRemappingPercent(byte color, byte percent) {
 	assert(_remapEndColor - color >= 0 && _remapEndColor - color < REMAP_COLOR_COUNT);
-	_remaps[_remapEndColor - color] = RemapParams(0, 0, 0, 0, percent, kRemappingByPercent);
+	_remaps[_remapEndColor - color] = RemapParams(0, 0, 0, 0, percent, kRemapByPercent);
 	initColorArrays(_remapEndColor - color);
 	_remapCount++;
 	_update = true;
@@ -72,7 +72,7 @@ void GfxRemap32::setRemappingPercent(byte color, byte percent) {
 
 void GfxRemap32::setRemappingToGray(byte color, byte gray) {
 	assert(_remapEndColor - color >= 0 && _remapEndColor - color < REMAP_COLOR_COUNT);
-	_remaps[_remapEndColor - color] = RemapParams(0, 0, 0, gray, 100, kRemappingToGray);
+	_remaps[_remapEndColor - color] = RemapParams(0, 0, 0, gray, 100, kRemapToGray);
 	initColorArrays(_remapEndColor - color);
 	_remapCount++;
 	_update = true;
@@ -80,7 +80,7 @@ void GfxRemap32::setRemappingToGray(byte color, byte gray) {
 
 void GfxRemap32::setRemappingToPercentGray(byte color, byte gray, byte percent) {
 	assert(_remapEndColor - color >= 0 && _remapEndColor - color < REMAP_COLOR_COUNT);
-	_remaps[_remapEndColor - color] = RemapParams(0, 0, 0, gray, percent, kRemappingToPercentGray);
+	_remaps[_remapEndColor - color] = RemapParams(0, 0, 0, gray, percent, kRemapToPercentGray);
 	initColorArrays(_remapEndColor - color);
 	_remapCount++;
 	_update = true;
@@ -94,13 +94,13 @@ void GfxRemap32::setNoMatchRange(byte from, byte count) {
 bool GfxRemap32::remapEnabled(byte color) const {
 	assert(_remapEndColor - color >= 0 && _remapEndColor - color < REMAP_COLOR_COUNT);
 	const byte index = _remapEndColor - color;
-	return (_remaps[index].type != kRemappingNone);
+	return (_remaps[index].type != kRemapNone);
 }
 
 byte GfxRemap32::remapColor(byte color, byte target) {
 	assert(_remapEndColor - color >= 0 && _remapEndColor - color < REMAP_COLOR_COUNT);
 	const byte index = _remapEndColor - color;
-	if (_remaps[index].type != kRemappingNone)
+	if (_remaps[index].type != kRemapNone)
 		return _remaps[index].remap[target];
 	else
 		return target;
@@ -127,9 +127,9 @@ bool GfxRemap32::updateRemap(byte index, bool palChanged) {
 	Common::fill(_targetChanged, _targetChanged + NON_REMAPPED_COLOR_COUNT, false);
 
 	switch (curRemap->type) {
-	case kRemappingNone:
+	case kRemapNone:
 		return false;
-	case kRemappingByRange:
+	case kRemapByRange:
 		for (int i = 0; i < NON_REMAPPED_COLOR_COUNT; i++)  {
 			if (curRemap->from <= i && i <= curRemap->to)
 				result = i + curRemap->base;
@@ -144,7 +144,7 @@ bool GfxRemap32::updateRemap(byte index, bool palChanged) {
 			curRemap->colorChanged[i] = true;
 		}
 		return changed;
-	case kRemappingByPercent:
+	case kRemapByPercent:
 		for (int i = 1; i < NON_REMAPPED_COLOR_COUNT; i++) {
 			// NOTE: This method uses nextPalette instead of curPalette
 			Color color = nextPalette->colors[i];
@@ -172,7 +172,7 @@ bool GfxRemap32::updateRemap(byte index, bool palChanged) {
 		Common::fill(curRemap->colorChanged, curRemap->colorChanged + NON_REMAPPED_COLOR_COUNT, false);
 		curRemap->oldPercent = curRemap->percent;
 		return changed;
-	case kRemappingToGray:
+	case kRemapToGray:
 		for (int i = 1; i < NON_REMAPPED_COLOR_COUNT; i++) {
 			Color color = curPalette->colors[i];
 
@@ -200,7 +200,7 @@ bool GfxRemap32::updateRemap(byte index, bool palChanged) {
 		Common::fill(curRemap->colorChanged, curRemap->colorChanged + NON_REMAPPED_COLOR_COUNT, false);
 		curRemap->oldGray = curRemap->gray;
 		return changed;
-	case kRemappingToPercentGray:
+	case kRemapToPercentGray:
 		for (int i = 1; i < NON_REMAPPED_COLOR_COUNT; i++) {
 			Color color = curPalette->colors[i];
 
diff --git a/engines/sci/graphics/remap32.h b/engines/sci/graphics/remap32.h
index ff871f6..3d5b67d 100644
--- a/engines/sci/graphics/remap32.h
+++ b/engines/sci/graphics/remap32.h
@@ -24,13 +24,20 @@
 #define SCI_GRAPHICS_REMAP32_H
 
 #include "common/array.h"
-#include "sci/graphics/remap.h"
 
 namespace Sci {
 
 #define REMAP_COLOR_COUNT 9
 #define NON_REMAPPED_COLOR_COUNT 236
 
+enum RemapType {
+	kRemapNone = 0,
+	kRemapByRange = 1,
+	kRemapByPercent = 2,
+	kRemapToGray = 3,
+	kRemapToPercentGray = 4
+};
+
 struct RemapParams {
 	byte from;
 	byte to;
@@ -39,7 +46,7 @@ struct RemapParams {
 	byte oldGray;
 	byte percent;
 	byte oldPercent;
-	ColorRemappingType type;
+	RemapType type;
 	Color curColor[256];
 	Color targetColor[256];
 	byte distance[256];
@@ -48,7 +55,7 @@ struct RemapParams {
 
 	RemapParams() {
 		from = to = base = gray = oldGray = percent = oldPercent = 0;
-		type = kRemappingNone;
+		type = kRemapNone;
 
 		// curColor and targetColor are initialized in GfxRemap32::initColorArrays
 		memset(curColor, 0, 256 * sizeof(Color));
@@ -59,7 +66,7 @@ struct RemapParams {
 		Common::fill(colorChanged, colorChanged + ARRAYSIZE(colorChanged), true);
 	}
 
-	RemapParams(byte from_, byte to_, byte base_, byte gray_, byte percent_, ColorRemappingType type_) {
+	RemapParams(byte from_, byte to_, byte base_, byte gray_, byte percent_, RemapType type_) {
 		from = from_;
 		to = to_;
 		base = base_;
diff --git a/engines/sci/graphics/view.h b/engines/sci/graphics/view.h
index 9159020..96b48c0 100644
--- a/engines/sci/graphics/view.h
+++ b/engines/sci/graphics/view.h
@@ -55,6 +55,7 @@ struct LoopInfo {
 
 class GfxScreen;
 class GfxPalette;
+class Resource;
 
 /**
  * View class, handles loading of view resources and drawing contained cels to screen


Commit: 0c799e1bd910231ba05c8ed7f182577314ac967b
    https://github.com/scummvm/scummvm/commit/0c799e1bd910231ba05c8ed7f182577314ac967b
Author: Colin Snover (github.com at zetafleet.com)
Date: 2016-07-01T00:18:32+02:00

Commit Message:
SCI32: Move matchColor to GfxRemap32

Changed paths:
    engines/sci/graphics/palette32.cpp
    engines/sci/graphics/palette32.h
    engines/sci/graphics/remap32.cpp
    engines/sci/graphics/remap32.h



diff --git a/engines/sci/graphics/palette32.cpp b/engines/sci/graphics/palette32.cpp
index 6c6234d..5c9bfd6 100644
--- a/engines/sci/graphics/palette32.cpp
+++ b/engines/sci/graphics/palette32.cpp
@@ -82,6 +82,11 @@ const Palette *GfxPalette32::getNextPalette() const {
 	return &_nextPalette;
 }
 
+const Palette *GfxPalette32::getCurrentPalette() const {
+	return &_sysPalette;
+}
+
+
 void GfxPalette32::submit(Palette &palette) {
 	// TODO: The resource manager in SCI32 retains raw data of palettes from
 	// the ResourceManager (ResourceMgr) through SegManager (MemoryMgr), and
@@ -178,49 +183,6 @@ void GfxPalette32::set(Palette *newPalette, bool force, bool forceRealMerge) {
 	submit(*newPalette);
 }
 
-// In SCI32 engine this method is SOLPalette::Match(Rgb24 *, int, int *, int *)
-// and is used by Remap
-// TODO: Anything that calls GfxPalette::matchColor(int, int, int) is going to
-// match using an algorithm from SCI16 engine right now. This needs to be
-// corrected in the future so either nothing calls
-// GfxPalette::matchColor(int, int, int), or it is fixed to match the other
-// SCI32 algorithms.
-int16 GfxPalette32::matchColor(const byte r, const byte g, const byte b, const int defaultDifference, int &lastCalculatedDifference, const bool *const matchTable) {
-	int16 bestIndex = -1;
-	int bestDifference = 0xFFFFF;
-	int difference = defaultDifference;
-
-	// SQ6 DOS really does check only the first 236 entries
-	for (int i = 0, channelDifference; i < 236; ++i) {
-		if (matchTable[i] == 0) {
-			continue;
-		}
-
-		difference = _sysPalette.colors[i].r - r;
-		difference *= difference;
-		if (bestDifference <= difference) {
-			continue;
-		}
-		channelDifference = _sysPalette.colors[i].g - g;
-		difference += channelDifference * channelDifference;
-		if (bestDifference <= difference) {
-			continue;
-		}
-		channelDifference = _sysPalette.colors[i].b - b;
-		difference += channelDifference * channelDifference;
-		if (bestDifference <= difference) {
-			continue;
-		}
-		bestDifference = difference;
-		bestIndex = i;
-	}
-
-	// NOTE: This value is only valid if the last index to
-	// perform a difference calculation was the best index
-	lastCalculatedDifference = difference;
-	return bestIndex;
-}
-
 bool GfxPalette32::updateForFrame() {
 	applyAll();
 	_versionUpdated = false;
diff --git a/engines/sci/graphics/palette32.h b/engines/sci/graphics/palette32.h
index 1902d00..ec75b58 100644
--- a/engines/sci/graphics/palette32.h
+++ b/engines/sci/graphics/palette32.h
@@ -114,11 +114,11 @@ private:
 public:
 	virtual void saveLoadWithSerializer(Common::Serializer &s) override;
 	const Palette *getNextPalette() const;
+	const Palette *getCurrentPalette() const;
 
 	bool kernelSetFromResource(GuiResourceId resourceId, bool force) override;
 	int16 kernelFindColor(uint16 r, uint16 g, uint16 b) override;
 	void set(Palette *newPalette, bool force, bool forceRealMerge = false) override;
-	int16 matchColor(const byte matchRed, const byte matchGreen, const byte matchBlue, const int defaultDifference, int &lastCalculatedDifference, const bool *const matchTable);
 
 	/**
 	 * Submits a palette to display. Entries marked as “used” in the
diff --git a/engines/sci/graphics/remap32.cpp b/engines/sci/graphics/remap32.cpp
index 774c4ca..ef27c84 100644
--- a/engines/sci/graphics/remap32.cpp
+++ b/engines/sci/graphics/remap32.cpp
@@ -269,7 +269,7 @@ bool GfxRemap32::applyRemap(byte index) {
 				continue;
 
 		int diff = 0;
-		int16 result = _palette->matchColor(targetColor.r, targetColor.g, targetColor.b, curRemap->distance[i], diff, unmappedColors);
+		int16 result = matchColor(targetColor.r, targetColor.g, targetColor.b, curRemap->distance[i], diff, unmappedColors);
 		if (result != -1 && curRemap->remap[i] != result)  {
 			changed = true;
 			curRemap->remap[i] = result;
@@ -291,4 +291,48 @@ bool GfxRemap32::remapAllTables(bool palChanged) {
 	return changed;
 }
 
+// In SCI32 engine this method is SOLPalette::Match(Rgb24 *, int, int *, int *)
+// and is used by Remap
+// TODO: Anything that calls GfxPalette::matchColor(int, int, int) is going to
+// match using an algorithm from SCI16 engine right now. This needs to be
+// corrected in the future so either nothing calls
+// GfxPalette::matchColor(int, int, int), or it is fixed to match the other
+// SCI32 algorithms.
+int16 GfxRemap32::matchColor(const byte r, const byte g, const byte b, const int defaultDifference, int &lastCalculatedDifference, const bool *const matchTable) const {
+	int16 bestIndex = -1;
+	int bestDifference = 0xFFFFF;
+	int difference = defaultDifference;
+	const Palette &_sysPalette = *g_sci->_gfxPalette32->getCurrentPalette();
+
+	// SQ6 DOS really does check only the first 236 entries
+	for (int i = 0, channelDifference; i < 236; ++i) {
+		if (matchTable[i] == 0) {
+			continue;
+		}
+
+		difference = _sysPalette.colors[i].r - r;
+		difference *= difference;
+		if (bestDifference <= difference) {
+			continue;
+		}
+		channelDifference = _sysPalette.colors[i].g - g;
+		difference += channelDifference * channelDifference;
+		if (bestDifference <= difference) {
+			continue;
+		}
+		channelDifference = _sysPalette.colors[i].b - b;
+		difference += channelDifference * channelDifference;
+		if (bestDifference <= difference) {
+			continue;
+		}
+		bestDifference = difference;
+		bestIndex = i;
+	}
+
+	// NOTE: This value is only valid if the last index to
+	// perform a difference calculation was the best index
+	lastCalculatedDifference = difference;
+	return bestIndex;
+}
+
 } // End of namespace Sci
diff --git a/engines/sci/graphics/remap32.h b/engines/sci/graphics/remap32.h
index 3d5b67d..52736f5 100644
--- a/engines/sci/graphics/remap32.h
+++ b/engines/sci/graphics/remap32.h
@@ -113,6 +113,7 @@ private:
 	void initColorArrays(byte index);
 	bool applyRemap(byte index);
 	bool updateRemap(byte index, bool palChanged);
+	int16 matchColor(const byte r, const byte g, const byte b, const int defaultDifference, int &lastCalculatedDifference, const bool *const matchTable) const;
 };
 
 } // End of namespace Sci


Commit: ca279390a36d0f87be134c6788c6420a748b99b8
    https://github.com/scummvm/scummvm/commit/ca279390a36d0f87be134c6788c6420a748b99b8
Author: Colin Snover (github.com at zetafleet.com)
Date: 2016-07-01T00:18:32+02:00

Commit Message:
SCI32: Fix broken Remap implementation

Remap would crash SCI2.1early games with 19 remap slots, and
did not actually work in most cases in SCI2.1mid+ games.

1. Avoid accidental corruption of values from the VM that may be
   valid when signed or larger than 8 bits
2. Fix bad `matchColor` function.
3. Remove unnecessary initialisation of SingleRemaps
4. Update architecture to more closely mirror how SSCI worked
5. Split large `apply` function into smaller units
6. Fix buffer overrun when loading a SCI2.1early game with remap
7. Warn instead of crashing with an error on invalid input (to
    match SSCI more closely)
8. Add save/load function

Changed paths:
    engines/sci/engine/kgraphics32.cpp
    engines/sci/engine/savegame.cpp
    engines/sci/engine/savegame.h
    engines/sci/graphics/frameout.cpp
    engines/sci/graphics/palette32.cpp
    engines/sci/graphics/palette32.h
    engines/sci/graphics/remap.h
    engines/sci/graphics/remap32.cpp
    engines/sci/graphics/remap32.h
    engines/sci/sci.cpp



diff --git a/engines/sci/engine/kgraphics32.cpp b/engines/sci/engine/kgraphics32.cpp
index e85d2a9..019a069 100644
--- a/engines/sci/engine/kgraphics32.cpp
+++ b/engines/sci/engine/kgraphics32.cpp
@@ -933,50 +933,62 @@ reg_t kRemapColors32(EngineState *s, int argc, reg_t *argv) {
 }
 
 reg_t kRemapColorsOff(EngineState *s, int argc, reg_t *argv) {
-	byte color = (argc >= 1) ? argv[0].toUint16() : 0;
-	g_sci->_gfxRemap32->remapOff(color);
+	if (argc == 0) {
+		g_sci->_gfxRemap32->remapAllOff();
+	} else {
+		const uint8 color = argv[0].toUint16();
+		g_sci->_gfxRemap32->remapOff(color);
+	}
 	return s->r_acc;
 }
 
 reg_t kRemapColorsByRange(EngineState *s, int argc, reg_t *argv) {
-	byte color = argv[0].toUint16();
-	byte from = argv[1].toUint16();
-	byte to = argv[2].toUint16();
-	byte base = argv[3].toUint16();
-	// The last parameter, depth, is unused
-	g_sci->_gfxRemap32->setRemappingRange(color, from, to, base);
+	const uint8 color = argv[0].toUint16();
+	const int16 from = argv[1].toSint16();
+	const int16 to = argv[2].toSint16();
+	const int16 base = argv[3].toSint16();
+	// NOTE: There is an optional last parameter after `base`
+	// which was only used by the priority map debugger, which
+	// does not exist in release versions of SSCI
+	g_sci->_gfxRemap32->remapByRange(color, from, to, base);
 	return s->r_acc;
 }
 
 reg_t kRemapColorsByPercent(EngineState *s, int argc, reg_t *argv) {
-	byte color = argv[0].toUint16();
-	byte percent = argv[1].toUint16();
-	// The last parameter, depth, is unused
-	g_sci->_gfxRemap32->setRemappingPercent(color, percent);
+	const uint8 color = argv[0].toUint16();
+	const int16 percent = argv[1].toSint16();
+	// NOTE: There is an optional last parameter after `percent`
+	// which was only used by the priority map debugger, which
+	// does not exist in release versions of SSCI
+	g_sci->_gfxRemap32->remapByPercent(color, percent);
 	return s->r_acc;
 }
 
 reg_t kRemapColorsToGray(EngineState *s, int argc, reg_t *argv) {
-	byte color = argv[0].toUint16();
-	byte gray = argv[1].toUint16();
-	// The last parameter, depth, is unused
-	g_sci->_gfxRemap32->setRemappingToGray(color, gray);
+	const uint8 color = argv[0].toUint16();
+	const int16 gray = argv[1].toSint16();
+	// NOTE: There is an optional last parameter after `gray`
+	// which was only used by the priority map debugger, which
+	// does not exist in release versions of SSCI
+	g_sci->_gfxRemap32->remapToGray(color, gray);
 	return s->r_acc;
 }
 
 reg_t kRemapColorsToPercentGray(EngineState *s, int argc, reg_t *argv) {
-	byte color = argv[0].toUint16();
-	byte gray = argv[1].toUint16();
-	byte percent = argv[2].toUint16();
-	// The last parameter, depth, is unused
-	g_sci->_gfxRemap32->setRemappingToPercentGray(color, gray, percent);
+	const uint8 color = argv[0].toUint16();
+	const int16 gray = argv[1].toSint16();
+	const int16 percent = argv[2].toSint16();
+	// NOTE: There is an optional last parameter after `percent`
+	// which was only used by the priority map debugger, which
+	// does not exist in release versions of SSCI
+	g_sci->_gfxRemap32->remapToPercentGray(color, gray, percent);
 	return s->r_acc;
 }
 
 reg_t kRemapColorsBlockRange(EngineState *s, int argc, reg_t *argv) {
-	byte from = argv[0].toUint16();
-	byte count = argv[1].toUint16();
-	g_sci->_gfxRemap32->setNoMatchRange(from, count);
+	const uint8 from = argv[0].toUint16();
+	const uint8 count = argv[1].toUint16();
+	g_sci->_gfxRemap32->blockRange(from, count);
 	return s->r_acc;
 }
 
diff --git a/engines/sci/engine/savegame.cpp b/engines/sci/engine/savegame.cpp
index 302f046..0972aec 100644
--- a/engines/sci/engine/savegame.cpp
+++ b/engines/sci/engine/savegame.cpp
@@ -48,8 +48,9 @@
 #include "sci/sound/music.h"
 
 #ifdef ENABLE_SCI32
-#include "sci/graphics/palette32.h"
 #include "sci/graphics/frameout.h"
+#include "sci/graphics/palette32.h"
+#include "sci/graphics/remap32.h"
 #endif
 
 namespace Sci {
@@ -807,6 +808,33 @@ void GfxPalette32::saveLoadWithSerializer(Common::Serializer &s) {
 		}
 	}
 }
+
+void GfxRemap32::saveLoadWithSerializer(Common::Serializer &s) {
+	if (s.getVersion() < 35) {
+		return;
+	}
+
+	s.syncAsByte(_numActiveRemaps);
+	s.syncAsByte(_blockedRangeStart);
+	s.syncAsSint16LE(_blockedRangeCount);
+
+	for (uint i = 0; i < _remaps.size(); ++i) {
+		SingleRemap &singleRemap = _remaps[i];
+		s.syncAsByte(singleRemap._type);
+		if (s.isLoading() && singleRemap._type != kRemapNone) {
+			singleRemap.reset();
+		}
+		s.syncAsByte(singleRemap._from);
+		s.syncAsByte(singleRemap._to);
+		s.syncAsByte(singleRemap._delta);
+		s.syncAsByte(singleRemap._percent);
+		s.syncAsByte(singleRemap._gray);
+	}
+
+	if (s.isLoading()) {
+		_needsUpdate = true;
+	}
+}
 #endif
 
 void GfxPorts::saveLoadWithSerializer(Common::Serializer &s) {
diff --git a/engines/sci/engine/savegame.h b/engines/sci/engine/savegame.h
index 459e992..43909ac 100644
--- a/engines/sci/engine/savegame.h
+++ b/engines/sci/engine/savegame.h
@@ -37,6 +37,7 @@ struct EngineState;
  *
  * Version - new/changed feature
  * =============================
+ *      35 - SCI32 remap
  *      34 - SCI32 palettes, and store play time in ticks
  *      33 - new overridePriority flag in MusicEntry
  *      32 - new playBed flag in MusicEntry
@@ -59,7 +60,7 @@ struct EngineState;
  */
 
 enum {
-	CURRENT_SAVEGAME_VERSION = 34,
+	CURRENT_SAVEGAME_VERSION = 35,
 	MINIMUM_SAVEGAME_VERSION = 14
 };
 
diff --git a/engines/sci/graphics/frameout.cpp b/engines/sci/graphics/frameout.cpp
index 9d3ab04..fd37020 100644
--- a/engines/sci/graphics/frameout.cpp
+++ b/engines/sci/graphics/frameout.cpp
@@ -1005,7 +1005,7 @@ void GfxFrameout::mergeToShowList(const Common::Rect &drawRect, RectList &showLi
 }
 
 void GfxFrameout::palMorphFrameOut(const int8 *styleRanges, const ShowStyleEntry *showStyle) {
-	Palette sourcePalette(*_palette->getNextPalette());
+	Palette sourcePalette(_palette->getNextPalette());
 	alterVmap(sourcePalette, sourcePalette, -1, styleRanges);
 
 	int16 prevRoom = g_sci->getEngineState()->variables[VAR_GLOBAL][12].toSint16();
@@ -1045,7 +1045,7 @@ void GfxFrameout::palMorphFrameOut(const int8 *styleRanges, const ShowStyleEntry
 		drawScreenItemList(screenItemLists[i]);
 	}
 
-	Palette nextPalette(*_palette->getNextPalette());
+	Palette nextPalette(_palette->getNextPalette());
 
 	if (prevRoom < 1000) {
 		for (int i = 0; i < ARRAYSIZE(sourcePalette.colors); ++i) {
diff --git a/engines/sci/graphics/palette32.cpp b/engines/sci/graphics/palette32.cpp
index 5c9bfd6..0840e82 100644
--- a/engines/sci/graphics/palette32.cpp
+++ b/engines/sci/graphics/palette32.cpp
@@ -78,15 +78,6 @@ inline void mergePaletteInternal(Palette *const to, const Palette *const from) {
 	}
 }
 
-const Palette *GfxPalette32::getNextPalette() const {
-	return &_nextPalette;
-}
-
-const Palette *GfxPalette32::getCurrentPalette() const {
-	return &_sysPalette;
-}
-
-
 void GfxPalette32::submit(Palette &palette) {
 	// TODO: The resource manager in SCI32 retains raw data of palettes from
 	// the ResourceManager (ResourceMgr) through SegManager (MemoryMgr), and
diff --git a/engines/sci/graphics/palette32.h b/engines/sci/graphics/palette32.h
index ec75b58..7dda53e 100644
--- a/engines/sci/graphics/palette32.h
+++ b/engines/sci/graphics/palette32.h
@@ -113,8 +113,8 @@ private:
 
 public:
 	virtual void saveLoadWithSerializer(Common::Serializer &s) override;
-	const Palette *getNextPalette() const;
-	const Palette *getCurrentPalette() const;
+	inline const Palette &getNextPalette() const { return _nextPalette; };
+	inline const Palette &getCurrentPalette() const { return _sysPalette; };
 
 	bool kernelSetFromResource(GuiResourceId resourceId, bool force) override;
 	int16 kernelFindColor(uint16 r, uint16 g, uint16 b) override;
@@ -240,6 +240,11 @@ private:
 	 * According to SCI engine code, when two cyclers overlap,
 	 * a fatal error has occurred and the engine will display
 	 * an error and then exit.
+	 *
+	 * The cycle map is also by the color remapping system to
+	 * avoid attempting to remap to palette entries that are
+	 * cycling (so won't be the expected color once the cycler
+	 * runs again).
 	 */
 	bool _cycleMap[256];
 	inline void clearCycleMap(uint16 fromColor, uint16 numColorsToClear);
@@ -257,7 +262,7 @@ public:
 	void cycleAllOff();
 	void applyAllCycles();
 	void applyCycles();
-	const bool *getCyclemap() { return _cycleMap; }
+	inline const bool *getCycleMap() const { return _cycleMap; }
 
 #pragma mark -
 #pragma mark Fading
diff --git a/engines/sci/graphics/remap.h b/engines/sci/graphics/remap.h
index a9cd76a..98177f6 100644
--- a/engines/sci/graphics/remap.h
+++ b/engines/sci/graphics/remap.h
@@ -24,7 +24,7 @@
 #define SCI_GRAPHICS_REMAP_H
 
 #include "common/array.h"
-#include "sci/graphics/helpers.h"
+#include "common/serializer.h"
 
 namespace Sci {
 
diff --git a/engines/sci/graphics/remap32.cpp b/engines/sci/graphics/remap32.cpp
index ef27c84..d5a2362 100644
--- a/engines/sci/graphics/remap32.cpp
+++ b/engines/sci/graphics/remap32.cpp
@@ -26,313 +26,443 @@
 
 namespace Sci {
 
-GfxRemap32::GfxRemap32(GfxPalette32 *palette) : _palette(palette) {
-	for (int i = 0; i < REMAP_COLOR_COUNT; i++)
-		_remaps[i] = RemapParams(0, 0, 0, 0, 100, kRemapNone);
-	_noMapStart = _noMapCount = 0;
-	_update = false;
-	_remapCount = 0;
-
-	// The remap range was 245 - 254 in SCI2, but was changed to 235 - 244 in SCI21 middle.
-	// All versions of KQ7 are using the older remap range semantics.
-	_remapEndColor = (getSciVersion() >= SCI_VERSION_2_1_MIDDLE || g_sci->getGameId() == GID_KQ7) ? 244 : 254;
+#pragma mark SingleRemap
+
+void SingleRemap::reset() {
+	_lastPercent = 100;
+	_lastGray = 0;
+
+	const uint8 remapStartColor = g_sci->_gfxRemap32->getStartColor();
+	const Palette &currentPalette = g_sci->_gfxPalette32->getCurrentPalette();
+	for (uint i = 0; i < remapStartColor; ++i) {
+		const Color &color = currentPalette.colors[i];
+		_remapColors[i] = i;
+		_originalColors[i] = color;
+		_originalColorsChanged[i] = true;
+		_idealColors[i] = color;
+		_idealColorsChanged[i] = false;
+		_matchDistances[i] = 0;
+	}
 }
 
-void GfxRemap32::remapOff(byte color) {
-	if (!color) {
-		for (int i = 0; i < REMAP_COLOR_COUNT; i++)
-			_remaps[i] = RemapParams(0, 0, 0, 0, 100, kRemapNone);
-
-		_remapCount = 0;
-	} else {
-		assert(_remapEndColor - color >= 0 && _remapEndColor - color < REMAP_COLOR_COUNT);
-		const byte index = _remapEndColor - color;
-		_remaps[index] = RemapParams(0, 0, 0, 0, 100, kRemapNone);
-		_remapCount--;
+bool SingleRemap::update() {
+	switch (_type) {
+	case kRemapNone:
+		break;
+	case kRemapByRange:
+		return updateRange();
+	case kRemapByPercent:
+		return updateBrightness();
+	case kRemapToGray:
+		return updateSaturation();
+	case kRemapToPercentGray:
+		return updateSaturationAndBrightness();
+	default:
+		error("Illegal remap type %d", _type);
 	}
 
-	_update = true;
+	return false;
 }
 
-void GfxRemap32::setRemappingRange(byte color, byte from, byte to, byte base) {
-	assert(_remapEndColor - color >= 0 && _remapEndColor - color < REMAP_COLOR_COUNT);
-	_remaps[_remapEndColor - color] = RemapParams(from, to, base, 0, 100, kRemapByRange);
-	initColorArrays(_remapEndColor - color);
-	_remapCount++;
-	_update = true;
-}
+bool SingleRemap::updateRange() {
+	const uint8 remapStartColor = g_sci->_gfxRemap32->getStartColor();
+	bool updated = false;
 
-void GfxRemap32::setRemappingPercent(byte color, byte percent) {
-	assert(_remapEndColor - color >= 0 && _remapEndColor - color < REMAP_COLOR_COUNT);
-	_remaps[_remapEndColor - color] = RemapParams(0, 0, 0, 0, percent, kRemapByPercent);
-	initColorArrays(_remapEndColor - color);
-	_remapCount++;
-	_update = true;
-}
+	for (uint i = 0; i < remapStartColor; ++i) {
+		uint8 targetColor;
+		if (_from <= i && i <= _to) {
+			targetColor = i + _delta;
+		} else {
+			targetColor = i;
+		}
 
-void GfxRemap32::setRemappingToGray(byte color, byte gray) {
-	assert(_remapEndColor - color >= 0 && _remapEndColor - color < REMAP_COLOR_COUNT);
-	_remaps[_remapEndColor - color] = RemapParams(0, 0, 0, gray, 100, kRemapToGray);
-	initColorArrays(_remapEndColor - color);
-	_remapCount++;
-	_update = true;
-}
+		if (_remapColors[i] != targetColor) {
+			updated = true;
+			_remapColors[i] = targetColor;
+		}
 
-void GfxRemap32::setRemappingToPercentGray(byte color, byte gray, byte percent) {
-	assert(_remapEndColor - color >= 0 && _remapEndColor - color < REMAP_COLOR_COUNT);
-	_remaps[_remapEndColor - color] = RemapParams(0, 0, 0, gray, percent, kRemapToPercentGray);
-	initColorArrays(_remapEndColor - color);
-	_remapCount++;
-	_update = true;
-}
+		_originalColorsChanged[i] = true;
+	}
 
-void GfxRemap32::setNoMatchRange(byte from, byte count) {
-	_noMapStart = from;
-	_noMapCount = count;
+	return updated;
 }
 
-bool GfxRemap32::remapEnabled(byte color) const {
-	assert(_remapEndColor - color >= 0 && _remapEndColor - color < REMAP_COLOR_COUNT);
-	const byte index = _remapEndColor - color;
-	return (_remaps[index].type != kRemapNone);
-}
+bool SingleRemap::updateBrightness() {
+	const uint8 remapStartColor = g_sci->_gfxRemap32->getStartColor();
+	const Palette &nextPalette = g_sci->_gfxPalette32->getNextPalette();
+	for (uint i = 1; i < remapStartColor; ++i) {
+		Color color(nextPalette.colors[i]);
 
-byte GfxRemap32::remapColor(byte color, byte target) {
-	assert(_remapEndColor - color >= 0 && _remapEndColor - color < REMAP_COLOR_COUNT);
-	const byte index = _remapEndColor - color;
-	if (_remaps[index].type != kRemapNone)
-		return _remaps[index].remap[target];
-	else
-		return target;
-}
+		if (_originalColors[i] != color) {
+			_originalColorsChanged[i] = true;
+			_originalColors[i] = color;
+		}
 
-void GfxRemap32::initColorArrays(byte index) {
-	Palette *curPalette = &_palette->_sysPalette;
-	RemapParams *curRemap = &_remaps[index];
+		if (_percent != _lastPercent || _originalColorsChanged[i]) {
+			// NOTE: SSCI checked if percent was over 100 and only
+			// then clipped values, but we always unconditionally
+			// ensure the result is in the correct range
+			color.r = MIN(255, (uint16)color.r * _percent / 100);
+			color.g = MIN(255, (uint16)color.g * _percent / 100);
+			color.b = MIN(255, (uint16)color.b * _percent / 100);
+
+			if (_idealColors[i] != color) {
+				_idealColorsChanged[i] = true;
+				_idealColors[i] = color;
+			}
+		}
+	}
 
-	memcpy(curRemap->curColor, curPalette->colors, NON_REMAPPED_COLOR_COUNT * sizeof(Color));
-	memcpy(curRemap->targetColor, curPalette->colors, NON_REMAPPED_COLOR_COUNT * sizeof(Color));
+	const bool updated = apply();
+	Common::fill(_originalColorsChanged, _originalColorsChanged + remapStartColor, false);
+	Common::fill(_idealColorsChanged, _idealColorsChanged + remapStartColor, false);
+	_lastPercent = _percent;
+	return updated;
 }
 
-bool GfxRemap32::updateRemap(byte index, bool palChanged) {
-	int result;
-	RemapParams *curRemap = &_remaps[index];
-	const Palette *curPalette = &_palette->_sysPalette;
-	const Palette *nextPalette = _palette->getNextPalette();
-	bool changed = false;
-
-	if (!_update && !palChanged)
-		return false;
-
-	Common::fill(_targetChanged, _targetChanged + NON_REMAPPED_COLOR_COUNT, false);
-
-	switch (curRemap->type) {
-	case kRemapNone:
-		return false;
-	case kRemapByRange:
-		for (int i = 0; i < NON_REMAPPED_COLOR_COUNT; i++)  {
-			if (curRemap->from <= i && i <= curRemap->to)
-				result = i + curRemap->base;
-			else
-				result = i;
-
-			if (curRemap->remap[i] != result) {
-				changed = true;
-				curRemap->remap[i] = result;
-			}
-
-			curRemap->colorChanged[i] = true;
+bool SingleRemap::updateSaturation() {
+	const uint8 remapStartColor = g_sci->_gfxRemap32->getStartColor();
+	const Palette &currentPalette = g_sci->_gfxPalette32->getCurrentPalette();
+	for (uint i = 1; i < remapStartColor; ++i) {
+		Color color(currentPalette.colors[i]);
+		if (_originalColors[i] != color) {
+			_originalColorsChanged[i] = true;
+			_originalColors[i] = color;
 		}
-		return changed;
-	case kRemapByPercent:
-		for (int i = 1; i < NON_REMAPPED_COLOR_COUNT; i++) {
-			// NOTE: This method uses nextPalette instead of curPalette
-			Color color = nextPalette->colors[i];
 
-			if (curRemap->curColor[i] != color) {
-				curRemap->colorChanged[i] = true;
-				curRemap->curColor[i] = color;
-			}
+		if (_gray != _lastGray || _originalColorsChanged[i]) {
+			const int luminosity = (((color.r * 77) + (color.g * 151) + (color.b * 28)) >> 8) * _percent / 100;
 
-			if (curRemap->percent != curRemap->oldPercent || curRemap->colorChanged[i])  {
-				byte red = CLIP<byte>(color.r * curRemap->percent / 100, 0, 255);
-				byte green = CLIP<byte>(color.g * curRemap->percent / 100, 0, 255);
-				byte blue = CLIP<byte>(color.b * curRemap->percent / 100, 0, 255);
-				byte used = curRemap->targetColor[i].used;
-
-				Color newColor = { used, red, green, blue };
-				if (curRemap->targetColor[i] != newColor)  {
-					_targetChanged[i] = true;
-					curRemap->targetColor[i] = newColor;
-				}
+			color.r = MIN(255, color.r - ((color.r - luminosity) * _gray / 100));
+			color.g = MIN(255, color.g - ((color.g - luminosity) * _gray / 100));
+			color.b = MIN(255, color.b - ((color.b - luminosity) * _gray / 100));
+
+			if (_idealColors[i] != color) {
+				_idealColorsChanged[i] = true;
+				_idealColors[i] = color;
 			}
 		}
-		
-		changed = applyRemap(index);
-		Common::fill(curRemap->colorChanged, curRemap->colorChanged + NON_REMAPPED_COLOR_COUNT, false);
-		curRemap->oldPercent = curRemap->percent;
-		return changed;
-	case kRemapToGray:
-		for (int i = 1; i < NON_REMAPPED_COLOR_COUNT; i++) {
-			Color color = curPalette->colors[i];
+	}
 
-			if (curRemap->curColor[i] != color) {
-				curRemap->colorChanged[i] = true;
-				curRemap->curColor[i] = color;
-			}
+	const bool updated = apply();
+	Common::fill(_originalColorsChanged, _originalColorsChanged + remapStartColor, false);
+	Common::fill(_idealColorsChanged, _idealColorsChanged + remapStartColor, false);
+	_lastGray = _gray;
+	return updated;
+}
 
-			if (curRemap->gray != curRemap->oldGray || curRemap->colorChanged[i])  {
-				byte lumosity = ((color.r * 77) + (color.g * 151) + (color.b * 28)) >> 8;
-				byte red = CLIP<byte>(color.r - ((color.r - lumosity) * curRemap->gray / 100), 0, 255);
-				byte green = CLIP<byte>(color.g - ((color.g - lumosity) * curRemap->gray / 100), 0, 255);
-				byte blue = CLIP<byte>(color.b - ((color.b - lumosity) * curRemap->gray / 100), 0, 255);
-				byte used = curRemap->targetColor[i].used;
-
-				Color newColor = { used, red, green, blue };
-				if (curRemap->targetColor[i] != newColor)  {
-					_targetChanged[i] = true;
-					curRemap->targetColor[i] = newColor;
-				}
-			}
+bool SingleRemap::updateSaturationAndBrightness() {
+	const uint8 remapStartColor = g_sci->_gfxRemap32->getStartColor();
+	const Palette &currentPalette = g_sci->_gfxPalette32->getCurrentPalette();
+	for (uint i = 1; i < remapStartColor; i++) {
+		Color color(currentPalette.colors[i]);
+		if (_originalColors[i] != color) {
+			_originalColorsChanged[i] = true;
+			_originalColors[i] = color;
 		}
 
-		changed = applyRemap(index);
-		Common::fill(curRemap->colorChanged, curRemap->colorChanged + NON_REMAPPED_COLOR_COUNT, false);
-		curRemap->oldGray = curRemap->gray;
-		return changed;
-	case kRemapToPercentGray:
-		for (int i = 1; i < NON_REMAPPED_COLOR_COUNT; i++) {
-			Color color = curPalette->colors[i];
+		if (_percent != _lastPercent || _gray != _lastGray || _originalColorsChanged[i]) {
+			const int luminosity = (((color.r * 77) + (color.g * 151) + (color.b * 28)) >> 8) * _percent / 100;
 
-			if (curRemap->curColor[i] != color) {
-				curRemap->colorChanged[i] = true;
-				curRemap->curColor[i] = color;
-			}
+			color.r = MIN(255, color.r - ((color.r - luminosity) * _gray) / 100);
+			color.g = MIN(255, color.g - ((color.g - luminosity) * _gray) / 100);
+			color.b = MIN(255, color.b - ((color.b - luminosity) * _gray) / 100);
 
-			if (curRemap->percent != curRemap->oldPercent || curRemap->gray != curRemap->oldGray || curRemap->colorChanged[i])  {
-				byte lumosity = ((color.r * 77) + (color.g * 151) + (color.b * 28)) >> 8;
-				lumosity = lumosity * curRemap->percent / 100;
-				byte red = CLIP<byte>(color.r - ((color.r - lumosity) * curRemap->gray / 100), 0, 255);
-				byte green = CLIP<byte>(color.g - ((color.g - lumosity) * curRemap->gray / 100), 0, 255);
-				byte blue = CLIP<byte>(color.b - ((color.b - lumosity) * curRemap->gray / 100), 0, 255);
-				byte used = curRemap->targetColor[i].used;
-
-				Color newColor = { used, red, green, blue };
-				if (curRemap->targetColor[i] != newColor)  {
-					_targetChanged[i] = true;
-					curRemap->targetColor[i] = newColor;
-				}
+			if (_idealColors[i] != color) {
+				_idealColorsChanged[i] = true;
+				_idealColors[i] = color;
 			}
 		}
-
-		changed = applyRemap(index);
-		Common::fill(curRemap->colorChanged, curRemap->colorChanged + NON_REMAPPED_COLOR_COUNT, false);
-		curRemap->oldPercent = curRemap->percent;
-		curRemap->oldGray = curRemap->gray;
-		return changed;
-	default:
-		return false;
 	}
-}
 
-static int colorDistance(Color a, Color b) {
-	int rDiff = (a.r - b.r) * (a.r - b.r);
-	int gDiff = (a.g - b.g) * (a.g - b.g);
-	int bDiff = (a.b - b.b) * (a.b - b.b);
-	return rDiff + gDiff + bDiff;
+	const bool updated = apply();
+	Common::fill(_originalColorsChanged, _originalColorsChanged + remapStartColor, false);
+	Common::fill(_idealColorsChanged, _idealColorsChanged + remapStartColor, false);
+	_lastPercent = _percent;
+	_lastGray = _gray;
+	return updated;
 }
 
-bool GfxRemap32::applyRemap(byte index) {
-	RemapParams *curRemap = &_remaps[index];
-	const bool *cycleMap = _palette->getCyclemap();
-	bool unmappedColors[NON_REMAPPED_COLOR_COUNT];
-	bool changed = false;
+bool SingleRemap::apply() {
+	const GfxRemap32 *const gfxRemap32 = g_sci->_gfxRemap32;
+	const uint8 remapStartColor = gfxRemap32->getStartColor();
+
+	// Blocked colors are not allowed to be used as target
+	// colors for the remap
+	bool blockedColors[236];
+	Common::fill(blockedColors, blockedColors + remapStartColor, false);
 
-	Common::fill(unmappedColors, unmappedColors + NON_REMAPPED_COLOR_COUNT, false);
-	if (_noMapCount)
-		Common::fill(unmappedColors + _noMapStart, unmappedColors + _noMapStart + _noMapCount, true);
+	const bool *const paletteCycleMap = g_sci->_gfxPalette32->getCycleMap();
 
-	for (int i = 0; i < NON_REMAPPED_COLOR_COUNT; i++)  {
-		if (cycleMap[i])
-			unmappedColors[i] = true;
+	const int16 blockedRangeCount = gfxRemap32->getBlockedRangeCount();
+	if (blockedRangeCount) {
+		const uint8 blockedRangeStart = gfxRemap32->getBlockedRangeStart();
+		Common::fill(blockedColors + blockedRangeStart, blockedColors + blockedRangeStart + blockedRangeCount, true);
 	}
 
-	for (int i = 1; i < NON_REMAPPED_COLOR_COUNT; i++)  {
-		Color targetColor = curRemap->targetColor[i];
-		bool colorChanged = curRemap->colorChanged[curRemap->remap[i]];
+	for (uint i = 0; i < remapStartColor; ++i) {
+		if (paletteCycleMap[i]) {
+			blockedColors[i] = true;
+		}
+	}
 
-		if (!_targetChanged[i] && !colorChanged)
-			continue;
+	// NOTE: SSCI did a loop over colors here to create a
+	// new array of updated, unblocked colors, but then
+	// never used it
 
-		if (_targetChanged[i] && colorChanged)
-			if (curRemap->distance[i] < 100 && colorDistance(targetColor, curRemap->curColor[curRemap->remap[i]]) <= curRemap->distance[i])
-				continue;
+	bool updated = false;
+	for (uint i = 1; i < remapStartColor; ++i) {
+		int distance;
 
-		int diff = 0;
-		int16 result = matchColor(targetColor.r, targetColor.g, targetColor.b, curRemap->distance[i], diff, unmappedColors);
-		if (result != -1 && curRemap->remap[i] != result)  {
-			changed = true;
-			curRemap->remap[i] = result;
-			curRemap->distance[i] = diff;
+		if (!_idealColorsChanged[i] && !_originalColorsChanged[_remapColors[i]]) {
+			continue;
 		}
-	}
 
-	return changed;
-}
+		if (
+			_idealColorsChanged[i] &&
+			_originalColorsChanged[_remapColors[i]] &&
+			_matchDistances[i] < 100 &&
+			colorDistance(_idealColors[i], _originalColors[_remapColors[i]]) <= _matchDistances[i]
+		) {
+			continue;
+		}
 
-bool GfxRemap32::remapAllTables(bool palChanged) {
-	bool changed = false;
+		const int16 bestColor = matchColor(_idealColors[i], _matchDistances[i], distance, blockedColors);
 
-	for (int i = 0; i < REMAP_COLOR_COUNT; i++) {
-		changed |= updateRemap(i, palChanged);
+		if (bestColor != -1 && _remapColors[i] != bestColor) {
+			updated = true;
+			_remapColors[i] = bestColor;
+			_matchDistances[i] = distance;
+		}
 	}
 
-	_update = false;
-	return changed;
+	return updated;
 }
 
-// In SCI32 engine this method is SOLPalette::Match(Rgb24 *, int, int *, int *)
-// and is used by Remap
-// TODO: Anything that calls GfxPalette::matchColor(int, int, int) is going to
-// match using an algorithm from SCI16 engine right now. This needs to be
-// corrected in the future so either nothing calls
-// GfxPalette::matchColor(int, int, int), or it is fixed to match the other
-// SCI32 algorithms.
-int16 GfxRemap32::matchColor(const byte r, const byte g, const byte b, const int defaultDifference, int &lastCalculatedDifference, const bool *const matchTable) const {
+int SingleRemap::colorDistance(const Color &a, const Color &b) const {
+	int channelDistance = a.r - b.r;
+	int distance = channelDistance * channelDistance;
+	channelDistance = a.g - b.g;
+	distance += channelDistance * channelDistance;
+	channelDistance = a.b - b.b;
+	distance += channelDistance * channelDistance;
+	return distance;
+}
+
+int16 SingleRemap::matchColor(const Color &color, const int minimumDistance, int &outDistance, const bool *const blockedIndexes) const {
 	int16 bestIndex = -1;
-	int bestDifference = 0xFFFFF;
-	int difference = defaultDifference;
-	const Palette &_sysPalette = *g_sci->_gfxPalette32->getCurrentPalette();
+	int bestDistance = 0xFFFFF;
+	int distance = minimumDistance;
+	const Palette &nextPalette = g_sci->_gfxPalette32->getNextPalette();
 
-	// SQ6 DOS really does check only the first 236 entries
-	for (int i = 0, channelDifference; i < 236; ++i) {
-		if (matchTable[i] == 0) {
+	for (uint i = 0, channelDistance; i < g_sci->_gfxRemap32->getStartColor(); ++i) {
+		if (blockedIndexes[i]) {
 			continue;
 		}
 
-		difference = _sysPalette.colors[i].r - r;
-		difference *= difference;
-		if (bestDifference <= difference) {
+		distance = nextPalette.colors[i].r - color.r;
+		distance *= distance;
+		if (bestDistance <= distance) {
 			continue;
 		}
-		channelDifference = _sysPalette.colors[i].g - g;
-		difference += channelDifference * channelDifference;
-		if (bestDifference <= difference) {
+		channelDistance = nextPalette.colors[i].g - color.g;
+		distance += channelDistance * channelDistance;
+		if (bestDistance <= distance) {
 			continue;
 		}
-		channelDifference = _sysPalette.colors[i].b - b;
-		difference += channelDifference * channelDifference;
-		if (bestDifference <= difference) {
+		channelDistance = nextPalette.colors[i].b - color.b;
+		distance += channelDistance * channelDistance;
+		if (bestDistance <= distance) {
 			continue;
 		}
-		bestDifference = difference;
+		bestDistance = distance;
 		bestIndex = i;
 	}
 
-	// NOTE: This value is only valid if the last index to
-	// perform a difference calculation was the best index
-	lastCalculatedDifference = difference;
+	// This value is only valid if the last index to
+	// perform a distance calculation was the best index
+	outDistance = distance;
 	return bestIndex;
 }
 
+#pragma mark -
+#pragma mark GfxRemap32
+
+GfxRemap32::GfxRemap32() :
+	_needsUpdate(false),
+	_blockedRangeStart(0),
+	_blockedRangeCount(0),
+	_remapStartColor(236),
+	_numActiveRemaps(0) {
+	// The `_remapStartColor` seems to always be 236 in SSCI,
+	// but if it is ever changed then the various C-style
+	// member arrays hard-coded to 236 need to be changed to
+	// match the highest possible value of `_remapStartColor`
+	assert(_remapStartColor == 236);
+
+	if (getSciVersion() >= SCI_VERSION_2_1_MIDDLE || g_sci->getGameId() == GID_KQ7) {
+		_remaps.resize(9);
+	} else {
+		_remaps.resize(19);
+	}
+
+	_remapEndColor = _remapStartColor + _remaps.size() - 1;
+}
+
+void GfxRemap32::remapOff(const uint8 color) {
+	if (color == 0) {
+		remapAllOff();
+		return;
+	}
+
+	// NOTE: SSCI simply ignored invalid input values, but
+	// we at least give a warning so games can be investigated
+	// for script bugs
+	if (color < _remapStartColor || color > _remapEndColor) {
+		warning("GfxRemap32::remapOff: %d out of remap range", color);
+		return;
+	}
+
+	const uint8 index = _remapEndColor - color;
+	SingleRemap &singleRemap = _remaps[index];
+	singleRemap._type = kRemapNone;
+	--_numActiveRemaps;
+	_needsUpdate = true;
+}
+
+void GfxRemap32::remapAllOff() {
+	for (uint i = 0, len = _remaps.size(); i < len; ++i) {
+		_remaps[i]._type = kRemapNone;
+	}
+
+	_numActiveRemaps = 0;
+	_needsUpdate = true;
+}
+
+void GfxRemap32::remapByRange(const uint8 color, const int16 from, const int16 to, const int16 delta) {
+	// NOTE: SSCI simply ignored invalid input values, but
+	// we at least give a warning so games can be investigated
+	// for script bugs
+	if (color < _remapStartColor || color > _remapEndColor) {
+		warning("GfxRemap32::remapByRange: %d out of remap range", color);
+		return;
+	}
+
+	if (from < 0) {
+		warning("GfxRemap32::remapByRange: attempt to remap negative color %d", from);
+		return;
+	}
+
+	if (to >= _remapStartColor) {
+		warning("GfxRemap32::remapByRange: attempt to remap into the remap zone at %d", to);
+		return;
+	}
+
+	const uint8 index = _remapEndColor - color;
+	SingleRemap &singleRemap = _remaps[index];
+
+	if (singleRemap._type == kRemapNone) {
+		++_numActiveRemaps;
+		singleRemap.reset();
+	}
+
+	singleRemap._from = from;
+	singleRemap._to = to;
+	singleRemap._delta = delta;
+	singleRemap._type = kRemapByRange;
+	_needsUpdate = true;
+}
+
+void GfxRemap32::remapByPercent(const uint8 color, const int16 percent) {
+	// NOTE: SSCI simply ignored invalid input values, but
+	// we at least give a warning so games can be investigated
+	// for script bugs
+	if (color < _remapStartColor || color > _remapEndColor) {
+		warning("GfxRemap32::remapByPercent: %d out of remap range", color);
+		return;
+	}
+
+	const uint8 index = _remapEndColor - color;
+	SingleRemap &singleRemap = _remaps[index];
+
+	if (singleRemap._type == kRemapNone) {
+		++_numActiveRemaps;
+		singleRemap.reset();
+	}
+
+	singleRemap._percent = percent;
+	singleRemap._type = kRemapByPercent;
+	_needsUpdate = true;
+}
+
+void GfxRemap32::remapToGray(const uint8 color, const int8 gray) {
+	// NOTE: SSCI simply ignored invalid input values, but
+	// we at least give a warning so games can be investigated
+	// for script bugs
+	if (color < _remapStartColor || color > _remapEndColor) {
+		warning("GfxRemap32::remapToGray: %d out of remap range", color);
+		return;
+	}
+
+	if (gray < 0 || gray > 100) {
+		error("RemapToGray percent out of range; gray = %d", gray);
+	}
+
+	const uint8 index = _remapEndColor - color;
+	SingleRemap &singleRemap = _remaps[index];
+
+	if (singleRemap._type == kRemapNone) {
+		++_numActiveRemaps;
+		singleRemap.reset();
+	}
+
+	singleRemap._gray = gray;
+	singleRemap._type = kRemapToGray;
+	_needsUpdate = true;
+}
+
+void GfxRemap32::remapToPercentGray(const uint8 color, const int16 gray, const int16 percent) {
+	// NOTE: SSCI simply ignored invalid input values, but
+	// we at least give a warning so games can be investigated
+	// for script bugs
+	if (color < _remapStartColor || color > _remapEndColor) {
+		warning("GfxRemap32::remapToPercentGray: %d out of remap range", color);
+		return;
+	}
+
+	const uint8 index = _remapEndColor - color;
+	SingleRemap &singleRemap = _remaps[index];
+
+	if (singleRemap._type == kRemapNone) {
+		++_numActiveRemaps;
+		singleRemap.reset();
+	}
+
+	singleRemap._percent = percent;
+	singleRemap._gray = gray;
+	singleRemap._type = kRemapToPercentGray;
+	_needsUpdate = true;
+}
+
+void GfxRemap32::blockRange(const uint8 from, const int16 count) {
+	_blockedRangeStart = from;
+	_blockedRangeCount = count;
+}
+
+bool GfxRemap32::remapAllTables(const bool paletteUpdated) {
+	if (!_needsUpdate && !paletteUpdated) {
+		return false;
+	}
+
+	bool updated = false;
+
+	for (SingleRemapsList::iterator it = _remaps.begin(); it != _remaps.end(); ++it) {
+		if (it->_type != kRemapNone) {
+			updated |= it->update();
+		}
+	}
+
+	_needsUpdate = false;
+	return updated;
+}
 } // End of namespace Sci
diff --git a/engines/sci/graphics/remap32.h b/engines/sci/graphics/remap32.h
index 52736f5..5f629d7 100644
--- a/engines/sci/graphics/remap32.h
+++ b/engines/sci/graphics/remap32.h
@@ -23,12 +23,13 @@
 #ifndef SCI_GRAPHICS_REMAP32_H
 #define SCI_GRAPHICS_REMAP32_H
 
+#include "common/algorithm.h"
 #include "common/array.h"
+#include "common/scummsys.h"
+#include "sci/graphics/helpers.h"
 
 namespace Sci {
-
-#define REMAP_COLOR_COUNT 9
-#define NON_REMAPPED_COLOR_COUNT 236
+class GfxPalette32;
 
 enum RemapType {
 	kRemapNone = 0,
@@ -38,84 +39,362 @@ enum RemapType {
 	kRemapToPercentGray = 4
 };
 
-struct RemapParams {
-	byte from;
-	byte to;
-	byte base;
-	byte gray;
-	byte oldGray;
-	byte percent;
-	byte oldPercent;
-	RemapType type;
-	Color curColor[256];
-	Color targetColor[256];
-	byte distance[256];
-	byte remap[256];
-	bool colorChanged[256];
-
-	RemapParams() {
-		from = to = base = gray = oldGray = percent = oldPercent = 0;
-		type = kRemapNone;
-
-		// curColor and targetColor are initialized in GfxRemap32::initColorArrays
-		memset(curColor, 0, 256 * sizeof(Color));
-		memset(targetColor, 0, 256 * sizeof(Color));
-		memset(distance, 0, 256);
-		for (int i = 0; i < NON_REMAPPED_COLOR_COUNT; i++)
-			remap[i] = i;
-		Common::fill(colorChanged, colorChanged + ARRAYSIZE(colorChanged), true);
-	}
+#pragma mark -
+#pragma mark SingleRemap
 
-	RemapParams(byte from_, byte to_, byte base_, byte gray_, byte percent_, RemapType type_) {
-		from = from_;
-		to = to_;
-		base = base_;
-		gray = oldGray = gray_;
-		percent = oldPercent = percent_;
-		type = type_;
-
-		// curColor and targetColor are initialized in GfxRemap32::initColorArrays
-		memset(curColor, 0, 256 * sizeof(Color));
-		memset(targetColor, 0, 256 * sizeof(Color));
-		memset(distance, 0, 256);
-		for (int i = 0; i < NON_REMAPPED_COLOR_COUNT; i++)
-			remap[i] = i;
-		Common::fill(colorChanged, colorChanged + ARRAYSIZE(colorChanged), true);
-	}
+/**
+ * SingleRemap objects each manage one remapping operation.
+ */
+class SingleRemap {
+public:
+	SingleRemap() : _type(kRemapNone) {}
+
+	/**
+	 * The type of remap.
+	 */
+	RemapType _type;
+
+	/**
+	 * The first color that should be shifted by a range
+	 * remap.
+	 */
+	uint8 _from;
+
+	/**
+	 * The last color that should be shifted a range remap.
+	 */
+	uint8 _to;
+
+	/**
+	 * The direction and amount that the colors should be
+	 * shifted in a range remap.
+	 */
+	int16 _delta;
+
+	/**
+	 * The difference in brightness that should be
+	 * applied by a brightness (percent) remap.
+	 *
+	 * This value may be be greater than 100, in
+	 * which case the color will be oversaturated.
+	 */
+	int16 _percent;
+
+	/**
+	 * The amount of desaturation that should be
+	 * applied by a saturation (gray) remap, where
+	 * 0 is full saturation and 100 is full
+	 * desaturation.
+	 */
+	uint8 _gray;
+
+	/**
+	 * The final array used by CelObj renderers to composite
+	 * remapped pixels to the screen buffer.
+	 *
+	 * Here is how it works:
+	 *
+	 * The source bitmap being rendered will have pixels
+	 * within the remap range (236-245 or 236-254), and the
+	 * target buffer will have colors in the non-remapped
+	 * range (0-235).
+	 *
+	 * To arrive at the correct color, first the source
+	 * pixel is used to look up the correct SingleRemap for
+	 * that pixel. Then, the final composited color is
+	 * looked up in this array using the target's pixel
+	 * color. In other words,
+	 * `target = _remaps[remapEndColor - source].remapColors[target]`.
+	 */
+	uint8 _remapColors[236];
+
+	/**
+	 * Resets this SingleRemap's color information to
+	 * default values.
+	 */
+	void reset();
+
+	/**
+	 * Recalculates and reapplies remap colors to the
+	 * `_remapColors` array.
+	 */
+	bool update();
+
+private:
+	/**
+	 * The previous brightness value. Used to
+	 * determine whether or not targetColors needs
+	 * to be updated.
+	 */
+	int16 _lastPercent;
+
+	/**
+	 * The previous saturation value. Used to
+	 * determine whether or not targetColors needs
+	 * to be updated.
+	 */
+	uint8 _lastGray;
+
+	/**
+	 * The colors from the current GfxPalette32 palette
+	 * before this SingleRemap is applied.
+	 */
+	Color _originalColors[236];
+
+	/**
+	 * Map of colors that changed in `_originalColors`
+	 * when this SingleRemap was updated. This map is
+	 * transient and gets reset to `false` after the
+	 * SingleRemap finishes updating.
+	 */
+	bool _originalColorsChanged[236];
+
+	/**
+	 * The ideal target RGB color values for each generated
+	 * remap color.
+	 */
+	Color _idealColors[236];
+
+	/**
+	 * Map of colors that changed in `_idealColors` when
+	 * this SingleRemap was updated. This map is transient
+	 * and gets reset to `false` after the SingleRemap
+	 * finishes applying.
+	 */
+	bool _idealColorsChanged[236];
+
+	/**
+	 * When applying a SingleRemap, finding an appropriate
+	 * color in the palette is the responsibility of a
+	 * distance function. Once a match is found, the
+	 * distance of that match is stored here so that the
+	 * next time the SingleRemap is applied, it can check
+	 * the distance from the previous application and avoid
+	 * triggering an expensive redraw of the entire screen
+	 * if the new palette value only changed slightly.
+	 */
+	int _matchDistances[236];
+
+	/**
+	 * Computes the final target values for a range remap
+	 * and applies them directly to the `_remaps` map.
+	 *
+	 * @note Was ByRange in SSCI.
+	 */
+	bool updateRange();
+
+	/**
+	 * Computes the intermediate target values for a
+	 * brightness remap and applies them indirectly via
+	 * the `apply` method.
+	 *
+	 * @note Was ByPercent in SSCI.
+	 */
+	bool updateBrightness();
+
+	/**
+	 * Computes the intermediate target values for a
+	 * saturation remap and applies them indirectly via
+	 * the `apply` method.
+	 *
+	 * @note Was ToGray in SSCI.
+	 */
+	bool updateSaturation();
+
+	/**
+	 * Computes the intermediate target values for a
+	 * saturation + brightness bitmap and applies them
+	 * indirectly via the `apply` method.
+	 *
+	 * @note Was ToPercentGray in SSCI.
+	 */
+	bool updateSaturationAndBrightness();
+
+	/**
+	 * Computes and applies the final values to the
+	 * `_remaps` map.
+	 *
+	 * @note In SSCI, a boolean array of changed values
+	 * was passed into this method, but this was done by
+	 * creating arrays on the stack in the caller. Instead
+	 * of doing this, we simply add another member property
+	 * `_idealColorsChanged` and use that instead.
+	 */
+	bool apply();
+
+	/**
+	 * Calculates the square distance of two colors.
+	 *
+	 * @note In SSCI this method is Rgb24::Dist, but it is
+	 * only used by SingleRemap.
+	 */
+	int colorDistance(const Color &a, const Color &b) const;
+
+	/**
+	 * Finds the closest index in the next palette matching
+	 * the given RGB color. Returns -1 if no match can be
+	 * found that is closer than `minimumDistance`.
+	 *
+	 * @note In SSCI, this method is SOLPalette::Match, but
+	 * this particular signature is only used by
+	 * SingleRemap.
+	 */
+	int16 matchColor(const Color &color, const int minimumDistance, int &outDistance, const bool *const blockedIndexes) const;
 };
 
-class GfxRemap32 {
+#pragma mark -
+#pragma mark GfxRemap32
+
+/**
+ * This class provides color remapping support for SCI32
+ * games.
+ */
+class GfxRemap32 : public Common::Serializable {
 public:
-	GfxRemap32(GfxPalette32 *palette);
-	~GfxRemap32() {}
-
-	void remapOff(byte color);
-	void setRemappingRange(byte color, byte from, byte to, byte base);
-	void setRemappingPercent(byte color, byte percent);
-	void setRemappingToGray(byte color, byte gray);
-	void setRemappingToPercentGray(byte color, byte gray, byte percent);
-	void setNoMatchRange(byte from, byte count);
-	bool remapEnabled(byte color) const;
-	byte remapColor(byte color, byte target);
-	bool remapAllTables(bool palChanged);
-	int getRemapCount() const { return _remapCount; }
-	int getStartColor() const { return _remapEndColor - REMAP_COLOR_COUNT + 1; }
-	int getEndColor() const { return _remapEndColor; }
+	GfxRemap32();
+
+	void saveLoadWithSerializer(Common::Serializer &s);
+
+	inline uint8 getRemapCount() const { return _numActiveRemaps; }
+	inline uint8 getStartColor() const { return _remapStartColor; }
+	inline uint8 getEndColor() const { return _remapEndColor; }
+	inline uint8 getBlockedRangeStart() const { return _blockedRangeStart; }
+	inline int16 getBlockedRangeCount() const { return _blockedRangeCount; }
+
+	/**
+	 * Turns off remapping of the given color. If `color` is
+	 * 0, all remaps are turned off.
+	 */
+	void remapOff(const uint8 color);
+
+	/**
+	 * Turns off all color remaps.
+	 */
+	void remapAllOff();
+
+	/**
+	 * Configures a SingleRemap for the remap color `color`.
+	 * The SingleRemap will shift palette colors between
+	 * `from` and `to` (inclusive) by `delta` palette
+	 * entries when the remap is applied.
+	 */
+	void remapByRange(const uint8 color, const int16 from, const int16 to, const int16 delta);
+
+	/**
+	 * Configures a SingleRemap for the remap color `color`
+	 * to modify the brightness of remapped colors by
+	 * `percent`.
+	 */
+	void remapByPercent(const uint8 color, const int16 percent);
+
+	/**
+	 * Configures a SingleRemap for the remap color `color`
+	 * to modify the saturation of remapped colors by
+	 * `gray`.
+	 */
+	void remapToGray(const uint8 color, const int8 gray);
+
+	/**
+	 * Configures a SingleRemap for the remap color `color`
+	 * to modify the brightness of remapped colors by
+	 * `percent`, and saturation of remapped colors by
+	 * `gray`.
+	 */
+	void remapToPercentGray(const uint8 color, const int16 gray, const int16 percent);
+
+	/**
+	 * Prevents GfxRemap32 from using the given range of
+	 * palette entries as potential remap targets.
+	 *
+	 * @NOTE Was DontMapToRange in SSCI.
+	 */
+	void blockRange(const uint8 from, const int16 count);
+
+	/**
+	 * Determines whether or not the given color has an
+	 * active remapper. If it does not, it is treated as a
+	 * skip color and the pixel is not drawn.
+	 *
+	 * @note SSCI uses a boolean array to decide whether a
+	 * a pixel is remapped, but it is possible to get the
+	 * same information from `_remaps`, as this function
+	 * does.
+	 * Presumably, the separate array was created for
+	 * performance reasons, since this is called a lot in
+	 * the most critical section of the renderer.
+	 */
+	inline bool remapEnabled(uint8 color) const {
+		const uint8 index = _remapEndColor - color;
+		assert(index < _remaps.size());
+		return (_remaps[index]._type != kRemapNone);
+	}
+
+	/**
+	 * Calculates the correct color for a target by looking
+	 * up the target color in the SingleRemap that controls
+	 * the given sourceColor. If there is no remap for the
+	 * given color, it will be treated as a skip color.
+	 */
+	inline uint8 remapColor(const uint8 sourceColor, const uint8 targetColor) const {
+		const uint8 index = _remapEndColor - sourceColor;
+		assert(index < _remaps.size());
+		const SingleRemap &singleRemap = _remaps[index];
+		assert(singleRemap._type != kRemapNone);
+		return singleRemap._remapColors[targetColor];
+	}
+
+	/**
+	 * Updates all active remaps in response to a palette
+	 * change or a remap settings change.
+	 *
+	 * `paletteChanged` is true if the next palette in
+	 * GfxPalette32 has been previously modified by other
+	 * palette operations.
+	 */
+	bool remapAllTables(const bool paletteUpdated);
+
 private:
-	GfxPalette32 *_palette;
-	RemapParams _remaps[REMAP_COLOR_COUNT];
-	bool _update;
-	byte _noMapStart, _noMapCount;
-	bool _targetChanged[NON_REMAPPED_COLOR_COUNT];
-	byte _remapEndColor;
-	int _remapCount;
-
-	void initColorArrays(byte index);
-	bool applyRemap(byte index);
-	bool updateRemap(byte index, bool palChanged);
-	int16 matchColor(const byte r, const byte g, const byte b, const int defaultDifference, int &lastCalculatedDifference, const bool *const matchTable) const;
-};
+	typedef Common::Array<SingleRemap> SingleRemapsList;
 
-} // End of namespace Sci
+	/**
+	 * The first index of the remap area in the system
+	 * palette.
+	 */
+	const uint8 _remapStartColor;
+
+	/**
+	 * The last index of the remap area in the system
+	 * palette.
+	 */
+	uint8 _remapEndColor;
 
+	/**
+	 * The number of currently active remaps.
+	 */
+	uint8 _numActiveRemaps;
+
+	/**
+	 * The list of SingleRemaps.
+	 */
+	SingleRemapsList _remaps;
+
+	/**
+	 * If true, indicates that one or more SingleRemaps were
+	 * reconfigured and all remaps need to be recalculated.
+	 */
+	bool _needsUpdate;
+
+	/**
+	 * The first color that is blocked from being used as a
+	 * remap target color.
+	 */
+	uint8 _blockedRangeStart;
+
+	/**
+	 * The size of the range of blocked colors. If zero,
+	 * all colors are potential targets for remapping.
+	 */
+	int16 _blockedRangeCount;
+};
+} // End of namespace Sci
 #endif
diff --git a/engines/sci/sci.cpp b/engines/sci/sci.cpp
index 243c12b..41fa144 100644
--- a/engines/sci/sci.cpp
+++ b/engines/sci/sci.cpp
@@ -700,7 +700,7 @@ void SciEngine::initGraphics() {
 	if (getSciVersion() >= SCI_VERSION_2) {
 		_gfxPalette32 = new GfxPalette32(_resMan, _gfxScreen);
 		_gfxPalette16 = _gfxPalette32;
-		_gfxRemap32 = new GfxRemap32(_gfxPalette32);
+		_gfxRemap32 = new GfxRemap32();
 	} else {
 #endif
 		_gfxPalette16 = new GfxPalette(_resMan, _gfxScreen);


Commit: 7edc5cd891d903e8ae114238745a965c319d7fa3
    https://github.com/scummvm/scummvm/commit/7edc5cd891d903e8ae114238745a965c319d7fa3
Author: Willem Jan Palenstijn (wjp at usecode.org)
Date: 2016-07-01T00:50:44+02:00

Commit Message:
Merge branch 'remap'

Changed paths:









More information about the Scummvm-git-logs mailing list