diff options
| author | Colin Snover | 2016-06-25 21:19:47 +0200 | 
|---|---|---|
| committer | Willem Jan Palenstijn | 2016-07-01 00:18:32 +0200 | 
| commit | ca279390a36d0f87be134c6788c6420a748b99b8 (patch) | |
| tree | 2ab248f2a8feedc15709d0611e7d5f691665a403 /engines | |
| parent | 0c799e1bd910231ba05c8ed7f182577314ac967b (diff) | |
| download | scummvm-rg350-ca279390a36d0f87be134c6788c6420a748b99b8.tar.gz scummvm-rg350-ca279390a36d0f87be134c6788c6420a748b99b8.tar.bz2 scummvm-rg350-ca279390a36d0f87be134c6788c6420a748b99b8.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.
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
Diffstat (limited to 'engines')
| -rw-r--r-- | engines/sci/engine/kgraphics32.cpp | 60 | ||||
| -rw-r--r-- | engines/sci/engine/savegame.cpp | 30 | ||||
| -rw-r--r-- | engines/sci/engine/savegame.h | 3 | ||||
| -rw-r--r-- | engines/sci/graphics/frameout.cpp | 4 | ||||
| -rw-r--r-- | engines/sci/graphics/palette32.cpp | 9 | ||||
| -rw-r--r-- | engines/sci/graphics/palette32.h | 11 | ||||
| -rw-r--r-- | engines/sci/graphics/remap.h | 2 | ||||
| -rw-r--r-- | engines/sci/graphics/remap32.cpp | 616 | ||||
| -rw-r--r-- | engines/sci/graphics/remap32.h | 431 | ||||
| -rw-r--r-- | engines/sci/sci.cpp | 2 | 
10 files changed, 807 insertions, 361 deletions
diff --git a/engines/sci/engine/kgraphics32.cpp b/engines/sci/engine/kgraphics32.cpp index e85d2a9877..019a06930c 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 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/frameout.cpp b/engines/sci/graphics/frameout.cpp index 9d3ab0463e..fd37020896 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 5c9bfd66e9..0840e82a40 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 ec75b58dd5..7dda53e5c1 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 a9cd76ae7b..98177f6d19 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 ef27c8491d..d5a2362f14 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 ¤tPalette = 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 ¤tPalette = 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 ¤tPalette = 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 52736f59d6..5f629d733e 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 243c12b5cf..41fa144b06 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);  | 
