/* 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 * `_idealColors` needs to be updated. */ int16 _lastPercent; /** * The previous saturation value. Used to determine whether or not * `_idealColors` 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 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; // At least KQ7 DOS uses remap colors that are outside the valid remap // range; in these cases, just treat those pixels as skip pixels (which // is how they would be treated in SSCI) if (index >= _remaps.size()) { return false; } 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); // SSCI never really properly handled attempts to draw to a target with // pixels above the remap color maximum. In RAMA, the cursor views have // a remap color outlining the cursor, and so get drawn into a target // surface filled with a skip color of 255. In SSCI, this causes the // remapped color to be read from some statically allocated, never // written memory and so always ends up being 0 (black). if (targetColor >= ARRAYSIZE(singleRemap._remapColors)) { return 0; } 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 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