diff options
author | Paul Gilbert | 2016-07-26 19:48:14 -0400 |
---|---|---|
committer | Paul Gilbert | 2016-07-26 19:48:14 -0400 |
commit | 504cf6ecb688a3f1c65a857bffd527d8b0e6ba63 (patch) | |
tree | 0c0d96d4061c11850c851f0fc981c75a58c20515 /engines/sci/graphics/remap32.cpp | |
parent | d8c28d15ae553d047b7e571f98727fa79ee143f3 (diff) | |
parent | e19922d181e775791f9105b8be7ff410770ede51 (diff) | |
download | scummvm-rg350-504cf6ecb688a3f1c65a857bffd527d8b0e6ba63.tar.gz scummvm-rg350-504cf6ecb688a3f1c65a857bffd527d8b0e6ba63.tar.bz2 scummvm-rg350-504cf6ecb688a3f1c65a857bffd527d8b0e6ba63.zip |
Merge branch 'master' into xeen
Diffstat (limited to 'engines/sci/graphics/remap32.cpp')
-rw-r--r-- | engines/sci/graphics/remap32.cpp | 468 |
1 files changed, 468 insertions, 0 deletions
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 ¤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; + } +} + +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 ¤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; + } + + 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 ¤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; + } + + 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 |