aboutsummaryrefslogtreecommitdiff
path: root/engines
diff options
context:
space:
mode:
authorColin Snover2016-06-25 14:19:47 -0500
committerColin Snover2016-06-26 12:42:58 -0500
commitcfda8b9ecd8a6e3c003abe533ea2e2981d8ba984 (patch)
tree1dd32560bd18981fa7cad096adc3f68c9f2758ca /engines
parentc5914eb80db79233766b2b6070ee92c637a18069 (diff)
downloadscummvm-rg350-cfda8b9ecd8a6e3c003abe533ea2e2981d8ba984.tar.gz
scummvm-rg350-cfda8b9ecd8a6e3c003abe533ea2e2981d8ba984.tar.bz2
scummvm-rg350-cfda8b9ecd8a6e3c003abe533ea2e2981d8ba984.zip
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. The SCI16 implementation was moved to its own separate file but was otherwise touched as little as possible, so may still have similar problems to the SCI32 code. 1. Split SCI16 and SCI32 code into separate files 2. Use -32 prefixes for SCI32 code and no prefix for SCI16 code, where possible, to match other existing code 3. Avoid accidental corruption of values from the VM that may be valid when signed or larger than 8 bits 4. Added documentation 5. Add missing remap CelObj calls 6. Inline where possible in performance-critical code paths 7. Fix bad `matchColor` function, and move it from GfxPalette to GfxRemap32 since it is only used by GfxRemap32 8. Fix bad capitalisation in getCycleMap 9. Remove unnecessary initialisation of SingleRemaps 10. Update architecture to more closely mirror how SSCI worked 11. Clarify the purpose of each type of remap type (and associated variable names) 12. Split large `apply` function into smaller units 13. Fix buffer overrun when loading a SCI2.1early game with remap 14. Remove use of `#define` constants 15. Warn instead of crashing with an error on invalid input (to match SSCI more closely) 16. Change the collision avoidance mechanism between the RemapType enum and remap kernel functions 17. Add save/load function
Diffstat (limited to 'engines')
-rw-r--r--engines/sci/engine/kernel.h16
-rw-r--r--engines/sci/engine/kernel_tables.h16
-rw-r--r--engines/sci/engine/kgraphics.cpp2
-rw-r--r--engines/sci/engine/kgraphics32.cpp75
-rw-r--r--engines/sci/engine/savegame.cpp30
-rw-r--r--engines/sci/engine/savegame.h3
-rw-r--r--engines/sci/graphics/celobj32.cpp101
-rw-r--r--engines/sci/graphics/frameout.cpp10
-rw-r--r--engines/sci/graphics/palette32.cpp49
-rw-r--r--engines/sci/graphics/palette32.h11
-rw-r--r--engines/sci/graphics/plane32.cpp2
-rw-r--r--engines/sci/graphics/remap.cpp291
-rw-r--r--engines/sci/graphics/remap.h107
-rw-r--r--engines/sci/graphics/remap32.cpp468
-rw-r--r--engines/sci/graphics/remap32.h400
-rw-r--r--engines/sci/graphics/view.h1
-rw-r--r--engines/sci/module.mk1
-rw-r--r--engines/sci/sci.cpp5
18 files changed, 1067 insertions, 521 deletions
diff --git a/engines/sci/engine/kernel.h b/engines/sci/engine/kernel.h
index b269e9789d..1202982986 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 19bee359c5..dacaafe757 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 73236b98ed..cae5a09789 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 9270c81c02..019a06930c 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
@@ -925,57 +926,69 @@ 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) {
- byte color = (argc >= 1) ? argv[0].toUint16() : 0;
- g_sci->_gfxRemap32->remapOff(color);
+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);
+ }
return s->r_acc;
}
-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);
+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);
return s->r_acc;
}
-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);
+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);
return s->r_acc;
}
-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);
+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);
return s->r_acc;
}
-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);
+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);
return s->r_acc;
}
-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);
+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);
return s->r_acc;
}
diff --git a/engines/sci/engine/savegame.cpp b/engines/sci/engine/savegame.cpp
index 302f046458..0972aec4a4 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 459e992e24..43909accf2 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/celobj32.cpp b/engines/sci/graphics/celobj32.cpp
index 77d333a717..af5ee3abbc 100644
--- a/engines/sci/graphics/celobj32.cpp
+++ b/engines/sci/graphics/celobj32.cpp
@@ -26,13 +26,12 @@
#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
+
CelScaler *CelObj::_scaler = nullptr;
void CelScaler::activateScaleTables(const Ratio &scaleX, const Ratio &scaleY) {
@@ -323,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) {
@@ -330,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;
@@ -523,6 +550,7 @@ void CelObj::submitPalette() const {
#pragma mark -
#pragma mark CelObj - Caching
+
int CelObj::_nextCacheId = 1;
CelCache *CelObj::_cache = nullptr;
@@ -624,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 {
@@ -670,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 {
@@ -715,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;
@@ -840,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;
}
}
@@ -853,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;
}
}
@@ -881,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;
@@ -1002,6 +1045,7 @@ byte *CelObjPic::getResPointer() const {
#pragma mark -
#pragma mark CelObjMem
+
CelObjMem::CelObjMem(const reg_t bitmapObject) {
_info.type = kCelTypeMem;
_info.bitmap = bitmapObject;
@@ -1031,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;
diff --git a/engines/sci/graphics/frameout.cpp b/engines/sci/graphics/frameout.cpp
index ccc56c0dc4..cbd61467b7 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"
@@ -862,7 +860,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();
@@ -904,7 +902,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 6844011675..0840e82a40 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 {
@@ -78,10 +78,6 @@ 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
@@ -178,49 +174,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 a5450776dc..70237c3b68 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;
- const Palette *getNextPalette() 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;
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,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/plane32.cpp b/engines/sci/graphics/plane32.cpp
index 470986fb3c..3f0b035738 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 ff49e52f13..2abf03ea29 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() {
@@ -104,277 +96,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 d012568f7f..98177f6d19 100644
--- a/engines/sci/graphics/remap.h
+++ b/engines/sci/graphics/remap.h
@@ -24,42 +24,36 @@
#define SCI_GRAPHICS_REMAP_H
#include "common/array.h"
-#include "sci/graphics/helpers.h"
+#include "common/serializer.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
-
/**
- * 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;
@@ -68,87 +62,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 0000000000..d5a2362f14
--- /dev/null
+++ b/engines/sci/graphics/remap32.cpp
@@ -0,0 +1,468 @@
+/* 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
new file mode 100644
index 0000000000..5f629d733e
--- /dev/null
+++ b/engines/sci/graphics/remap32.h
@@ -0,0 +1,400 @@
+/* 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 91590208c1..96b48c0477 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
diff --git a/engines/sci/module.mk b/engines/sci/module.mk
index ba6c853075..5d54e2a52c 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 b80456c09c..41fa144b06 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
@@ -699,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);