2016-01-07 02:00:28 +00:00
|
|
|
|
/* 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 "common/file.h"
|
2016-01-14 16:50:41 +00:00
|
|
|
|
#include "common/system.h"
|
|
|
|
|
#include "graphics/palette.h"
|
2016-01-07 02:00:28 +00:00
|
|
|
|
|
|
|
|
|
#include "sci/sci.h"
|
2016-01-14 16:50:41 +00:00
|
|
|
|
#include "sci/event.h"
|
|
|
|
|
#include "sci/resource.h"
|
2016-01-07 02:00:28 +00:00
|
|
|
|
#include "sci/graphics/palette32.h"
|
2016-03-11 03:10:32 +00:00
|
|
|
|
#include "sci/graphics/remap.h"
|
2016-01-14 16:50:41 +00:00
|
|
|
|
#include "sci/graphics/screen.h"
|
2016-01-07 02:00:28 +00:00
|
|
|
|
|
|
|
|
|
namespace Sci {
|
2016-02-14 11:50:04 +00:00
|
|
|
|
|
2016-01-07 02:00:28 +00:00
|
|
|
|
GfxPalette32::GfxPalette32(ResourceManager *resMan, GfxScreen *screen)
|
2016-01-14 16:50:41 +00:00
|
|
|
|
: GfxPalette(resMan, screen),
|
2016-02-19 03:09:15 +00:00
|
|
|
|
// Palette versioning
|
|
|
|
|
_version(1),
|
|
|
|
|
_versionUpdated(false),
|
|
|
|
|
_sourcePalette(_sysPalette),
|
|
|
|
|
_nextPalette(_sysPalette),
|
|
|
|
|
// Clut
|
2016-01-14 16:50:41 +00:00
|
|
|
|
_clutTable(nullptr),
|
|
|
|
|
// Palette varying
|
2016-02-19 03:09:15 +00:00
|
|
|
|
_varyStartPalette(nullptr),
|
|
|
|
|
_varyTargetPalette(nullptr),
|
|
|
|
|
_varyFromColor(0),
|
|
|
|
|
_varyToColor(255),
|
2016-02-14 11:50:04 +00:00
|
|
|
|
_varyLastTick(0),
|
2016-02-19 03:09:15 +00:00
|
|
|
|
_varyTime(0),
|
|
|
|
|
_varyDirection(0),
|
|
|
|
|
_varyTargetPercent(0),
|
|
|
|
|
_varyNumTimesPaused(0),
|
|
|
|
|
// Palette cycling
|
|
|
|
|
_cyclers(),
|
|
|
|
|
_cycleMap() {
|
2016-02-14 11:50:04 +00:00
|
|
|
|
_varyPercent = _varyTargetPercent;
|
2016-03-08 05:47:44 +00:00
|
|
|
|
for (int i = 0, len = ARRAYSIZE(_fadeTable); i < len; ++i) {
|
|
|
|
|
_fadeTable[i] = 100;
|
|
|
|
|
}
|
2016-01-14 16:50:41 +00:00
|
|
|
|
// NOTE: In SCI engine, the palette manager constructor loads
|
|
|
|
|
// the default palette, but in ScummVM this initialisation
|
|
|
|
|
// is performed by SciEngine::run; see r49523 for details
|
2016-01-18 06:12:47 +00:00
|
|
|
|
}
|
2016-01-07 02:00:28 +00:00
|
|
|
|
|
|
|
|
|
GfxPalette32::~GfxPalette32() {
|
|
|
|
|
unloadClut();
|
2016-01-14 16:50:41 +00:00
|
|
|
|
varyOff();
|
2016-01-07 02:00:28 +00:00
|
|
|
|
cycleAllOff();
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-14 16:50:41 +00:00
|
|
|
|
inline void mergePaletteInternal(Palette *const to, const Palette *const from) {
|
2016-03-08 05:48:13 +00:00
|
|
|
|
for (int i = 0, len = ARRAYSIZE(to->colors); i < len; ++i) {
|
2016-01-14 16:50:41 +00:00
|
|
|
|
if (from->colors[i].used) {
|
|
|
|
|
to->colors[i] = from->colors[i];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-18 06:12:47 +00:00
|
|
|
|
const Palette *GfxPalette32::getNextPalette() const {
|
|
|
|
|
return &_nextPalette;
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-14 16:50:41 +00:00
|
|
|
|
void GfxPalette32::submit(Palette &palette) {
|
|
|
|
|
// TODO: The resource manager in SCI32 retains raw data of palettes from
|
|
|
|
|
// the ResourceManager (ResourceMgr) through SegManager (MemoryMgr), and
|
|
|
|
|
// the version number for submitted palettes is set in the raw palette
|
|
|
|
|
// data in memory as an int at an offset
|
|
|
|
|
// `rawData + *rawData[0x0a] * 2 + 31`. However, ScummVM does not retain
|
|
|
|
|
// resource data like this, so this versioning code, while accurate to
|
|
|
|
|
// the original engine, does not do much.
|
|
|
|
|
// (Hopefully this was an optimisation mechanism in SCI engine and not a
|
|
|
|
|
// clever thing to keep the same palette submitted many times from
|
|
|
|
|
// overwriting other palette entries.)
|
|
|
|
|
if (palette.timestamp == _version) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Palette oldSourcePalette(_sourcePalette);
|
|
|
|
|
mergePaletteInternal(&_sourcePalette, &palette);
|
|
|
|
|
|
|
|
|
|
if (!_versionUpdated && _sourcePalette != oldSourcePalette) {
|
|
|
|
|
++_version;
|
|
|
|
|
_versionUpdated = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Technically this information is supposed to be persisted through a
|
|
|
|
|
// HunkPalette object; right now it would just be lost once the temporary
|
|
|
|
|
// palette was destroyed.
|
|
|
|
|
palette.timestamp = _version;
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-14 23:53:12 +00:00
|
|
|
|
bool GfxPalette32::kernelSetFromResource(GuiResourceId resourceId, bool force) {
|
2016-01-14 16:50:41 +00:00
|
|
|
|
// TODO: In SCI32, palettes that come from resources come in as
|
|
|
|
|
// HunkPalette objects, not SOLPalette objects. The HunkPalettes
|
|
|
|
|
// have some extra persistence stuff associated with them, such that
|
|
|
|
|
// when they are passed to GfxPalette32::submit, they would get the
|
|
|
|
|
// version number of GfxPalette32 assigned to them.
|
|
|
|
|
Palette palette;
|
|
|
|
|
|
|
|
|
|
if (createPaletteFromResourceInternal(resourceId, &palette)) {
|
|
|
|
|
submit(palette);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-14 16:56:58 +00:00
|
|
|
|
// In SCI32 engine this method is SOLPalette::Match(Rgb24 *)
|
|
|
|
|
// and is called as PaletteMgr.Current().Match(color)
|
2016-01-14 23:53:12 +00:00
|
|
|
|
int16 GfxPalette32::kernelFindColor(uint16 r, uint16 g, uint16 b) {
|
2016-01-14 16:56:58 +00:00
|
|
|
|
// SQ6 SCI32 engine takes the 16-bit r, g, b arguments from the
|
|
|
|
|
// VM and puts them into al, ah, dl. For compatibility, make sure
|
|
|
|
|
// to discard any high bits here too
|
|
|
|
|
r = r & 0xFF;
|
|
|
|
|
g = g & 0xFF;
|
|
|
|
|
b = b & 0xFF;
|
|
|
|
|
int16 bestIndex = 0;
|
|
|
|
|
int bestDifference = 0xFFFFF;
|
|
|
|
|
int difference;
|
|
|
|
|
|
|
|
|
|
// SQ6 DOS really does check only the first 236 entries
|
|
|
|
|
for (int i = 0, channelDifference; i < 236; ++i) {
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return bestIndex;
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-14 16:50:41 +00:00
|
|
|
|
// TODO: set is overridden for the time being to send palettes coming from
|
|
|
|
|
// various draw methods like GfxPicture::drawSci32Vga and GfxView::draw to
|
|
|
|
|
// _nextPalette instead of _sysPalette. In the SCI32 engine, CelObj palettes
|
|
|
|
|
// (which are stored as Hunk palettes) are submitted by GraphicsMgr::FrameOut
|
|
|
|
|
// to PaletteMgr::Submit by way of calls to CelObj::SubmitPalette.
|
|
|
|
|
// GfxPalette::set is very similar to GfxPalette32::submit, except that SCI32
|
|
|
|
|
// does not do any fancy best-fit merging and so does not accept arguments
|
|
|
|
|
// like `force` and `forceRealMerge`.
|
2016-01-14 23:53:12 +00:00
|
|
|
|
void GfxPalette32::set(Palette *newPalette, bool force, bool forceRealMerge) {
|
2016-01-14 16:50:41 +00:00
|
|
|
|
submit(*newPalette);
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-14 16:56:58 +00:00
|
|
|
|
// 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;
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-18 06:12:47 +00:00
|
|
|
|
bool GfxPalette32::updateForFrame() {
|
2016-01-14 16:50:41 +00:00
|
|
|
|
applyAll();
|
|
|
|
|
_versionUpdated = false;
|
2016-03-11 03:10:32 +00:00
|
|
|
|
return g_sci->_gfxRemap32->remapAllTables(_nextPalette != _sysPalette);
|
2016-01-18 06:12:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GfxPalette32::updateFFrame() {
|
|
|
|
|
for (int i = 0; i < ARRAYSIZE(_nextPalette.colors); ++i) {
|
|
|
|
|
_nextPalette.colors[i] = _sourcePalette.colors[i];
|
|
|
|
|
}
|
|
|
|
|
_versionUpdated = false;
|
2016-03-11 03:10:32 +00:00
|
|
|
|
g_sci->_gfxRemap32->remapAllTables(_nextPalette != _sysPalette);
|
2016-01-14 16:50:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GfxPalette32::updateHardware() {
|
|
|
|
|
if (_sysPalette == _nextPalette) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
byte bpal[3 * 256];
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < ARRAYSIZE(_sysPalette.colors); ++i) {
|
|
|
|
|
_sysPalette.colors[i] = _nextPalette.colors[i];
|
|
|
|
|
|
|
|
|
|
// NOTE: If the brightness option in the user configuration file is set,
|
|
|
|
|
// SCI engine adjusts palette brightnesses here by mapping RGB values to values
|
|
|
|
|
// in some hard-coded brightness tables. There is no reason on modern hardware
|
|
|
|
|
// to implement this, unless it is discovered that some game uses a non-standard
|
|
|
|
|
// brightness setting by default
|
|
|
|
|
if (_sysPalette.colors[i].used) {
|
|
|
|
|
bpal[i * 3 ] = _sysPalette.colors[i].r;
|
|
|
|
|
bpal[i * 3 + 1] = _sysPalette.colors[i].g;
|
|
|
|
|
bpal[i * 3 + 2] = _sysPalette.colors[i].b;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
g_system->getPaletteManager()->setPalette(bpal, 0, 256);
|
|
|
|
|
g_sci->getEventManager()->updateScreen();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GfxPalette32::applyAll() {
|
|
|
|
|
applyVary();
|
|
|
|
|
applyCycles();
|
|
|
|
|
applyFade();
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-19 03:09:15 +00:00
|
|
|
|
#pragma mark -
|
|
|
|
|
#pragma mark Colour look-up
|
2016-01-14 16:50:41 +00:00
|
|
|
|
|
2016-01-07 02:00:28 +00:00
|
|
|
|
bool GfxPalette32::loadClut(uint16 clutId) {
|
|
|
|
|
// loadClut() will load a color lookup table from a clu file and set
|
|
|
|
|
// the palette found in the file. This is to be used with Phantasmagoria 2.
|
|
|
|
|
|
|
|
|
|
unloadClut();
|
|
|
|
|
|
|
|
|
|
Common::String filename = Common::String::format("%d.clu", clutId);
|
|
|
|
|
Common::File clut;
|
|
|
|
|
|
|
|
|
|
if (!clut.open(filename) || clut.size() != 0x10000 + 236 * 3)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
// Read in the lookup table
|
|
|
|
|
// It maps each RGB565 color to a palette index
|
|
|
|
|
_clutTable = new byte[0x10000];
|
|
|
|
|
clut.read(_clutTable, 0x10000);
|
|
|
|
|
|
|
|
|
|
Palette pal;
|
|
|
|
|
memset(&pal, 0, sizeof(Palette));
|
|
|
|
|
|
|
|
|
|
// Setup 1:1 mapping
|
|
|
|
|
for (int i = 0; i < 256; i++) {
|
|
|
|
|
pal.mapping[i] = i;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Now load in the palette
|
|
|
|
|
for (int i = 1; i <= 236; i++) {
|
|
|
|
|
pal.colors[i].used = 1;
|
|
|
|
|
pal.colors[i].r = clut.readByte();
|
|
|
|
|
pal.colors[i].g = clut.readByte();
|
|
|
|
|
pal.colors[i].b = clut.readByte();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
set(&pal, true);
|
|
|
|
|
setOnScreen();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
byte GfxPalette32::matchClutColor(uint16 color) {
|
|
|
|
|
// Match a color in RGB565 format to a palette index based on the loaded CLUT
|
|
|
|
|
assert(_clutTable);
|
|
|
|
|
return _clutTable[color];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GfxPalette32::unloadClut() {
|
|
|
|
|
// This will only unload the actual table, but not reset any palette
|
|
|
|
|
delete[] _clutTable;
|
2016-01-14 16:58:37 +00:00
|
|
|
|
_clutTable = nullptr;
|
2016-01-07 02:00:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-02-19 03:09:15 +00:00
|
|
|
|
#pragma mark -
|
|
|
|
|
#pragma mark Varying
|
2016-01-14 16:50:41 +00:00
|
|
|
|
|
|
|
|
|
inline bool GfxPalette32::createPaletteFromResourceInternal(const GuiResourceId paletteId, Palette *const out) const {
|
|
|
|
|
Resource *palResource = _resMan->findResource(ResourceId(kResourceTypePalette, paletteId), false);
|
|
|
|
|
|
|
|
|
|
if (!palResource) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
createFromData(palResource->data, palResource->size, out);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline Palette GfxPalette32::getPaletteFromResourceInternal(const GuiResourceId paletteId) const {
|
|
|
|
|
Palette palette;
|
|
|
|
|
if (!createPaletteFromResourceInternal(paletteId, &palette)) {
|
|
|
|
|
error("Could not load vary target %d", paletteId);
|
|
|
|
|
}
|
|
|
|
|
return palette;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline void GfxPalette32::setVaryTimeInternal(const int16 percent, const int time) {
|
|
|
|
|
_varyLastTick = g_sci->getTickCount();
|
|
|
|
|
if (!time || _varyPercent == percent) {
|
|
|
|
|
_varyDirection = 0;
|
|
|
|
|
_varyTargetPercent = _varyPercent = percent;
|
|
|
|
|
} else {
|
|
|
|
|
_varyTime = time / (percent - _varyPercent);
|
|
|
|
|
_varyTargetPercent = percent;
|
|
|
|
|
|
|
|
|
|
if (_varyTime > 0) {
|
|
|
|
|
_varyDirection = 1;
|
|
|
|
|
} else if (_varyTime < 0) {
|
|
|
|
|
_varyDirection = -1;
|
|
|
|
|
_varyTime = -_varyTime;
|
|
|
|
|
} else {
|
|
|
|
|
_varyDirection = 0;
|
|
|
|
|
_varyTargetPercent = _varyPercent = percent;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO: This gets called *a lot* in at least the first scene
|
|
|
|
|
// of SQ6. Optimisation would not be the worst idea in the world.
|
|
|
|
|
void GfxPalette32::kernelPalVarySet(const GuiResourceId paletteId, const int16 percent, const int time, const int16 fromColor, const int16 toColor) {
|
|
|
|
|
Palette palette = getPaletteFromResourceInternal(paletteId);
|
|
|
|
|
setVary(&palette, percent, time, fromColor, toColor);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GfxPalette32::kernelPalVaryMergeTarget(GuiResourceId paletteId) {
|
|
|
|
|
Palette palette = getPaletteFromResourceInternal(paletteId);
|
|
|
|
|
mergeTarget(&palette);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GfxPalette32::kernelPalVarySetTarget(GuiResourceId paletteId) {
|
|
|
|
|
Palette palette = getPaletteFromResourceInternal(paletteId);
|
|
|
|
|
setTarget(&palette);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GfxPalette32::kernelPalVarySetStart(GuiResourceId paletteId) {
|
|
|
|
|
Palette palette = getPaletteFromResourceInternal(paletteId);
|
|
|
|
|
setStart(&palette);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GfxPalette32::kernelPalVaryMergeStart(GuiResourceId paletteId) {
|
|
|
|
|
Palette palette = getPaletteFromResourceInternal(paletteId);
|
|
|
|
|
mergeStart(&palette);
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-14 23:53:12 +00:00
|
|
|
|
void GfxPalette32::kernelPalVaryPause(bool pause) {
|
2016-01-14 16:50:41 +00:00
|
|
|
|
if (pause) {
|
|
|
|
|
varyPause();
|
|
|
|
|
} else {
|
|
|
|
|
varyOn();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GfxPalette32::setVary(const Palette *const target, const int16 percent, const int time, const int16 fromColor, const int16 toColor) {
|
|
|
|
|
setTarget(target);
|
|
|
|
|
setVaryTimeInternal(percent, time);
|
|
|
|
|
|
|
|
|
|
if (fromColor > -1) {
|
|
|
|
|
_varyFromColor = fromColor;
|
|
|
|
|
}
|
|
|
|
|
if (toColor > -1) {
|
|
|
|
|
assert(toColor < 256);
|
|
|
|
|
_varyToColor = toColor;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GfxPalette32::setVaryPercent(const int16 percent, const int time, const int16 fromColor, const int16 fromColorAlternate) {
|
|
|
|
|
if (_varyTargetPalette != nullptr) {
|
|
|
|
|
setVaryTimeInternal(percent, time);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This looks like a mistake in the actual SCI engine (both SQ6 and Lighthouse);
|
|
|
|
|
// the values are always hardcoded to -1 in kPalVary, so this code can never
|
|
|
|
|
// actually be executed
|
|
|
|
|
if (fromColor > -1) {
|
|
|
|
|
_varyFromColor = fromColor;
|
|
|
|
|
}
|
|
|
|
|
if (fromColorAlternate > -1) {
|
|
|
|
|
_varyFromColor = fromColorAlternate;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int16 GfxPalette32::getVaryPercent() const {
|
2016-01-18 06:12:47 +00:00
|
|
|
|
return ABS(_varyPercent);
|
2016-01-14 16:50:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GfxPalette32::varyOff() {
|
|
|
|
|
_varyNumTimesPaused = 0;
|
|
|
|
|
_varyPercent = _varyTargetPercent = 0;
|
|
|
|
|
_varyFromColor = 0;
|
|
|
|
|
_varyToColor = 255;
|
|
|
|
|
_varyDirection = 0;
|
|
|
|
|
|
|
|
|
|
if (_varyTargetPalette != nullptr) {
|
|
|
|
|
delete _varyTargetPalette;
|
|
|
|
|
_varyTargetPalette = nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (_varyStartPalette != nullptr) {
|
|
|
|
|
delete _varyStartPalette;
|
|
|
|
|
_varyStartPalette = nullptr;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GfxPalette32::mergeTarget(const Palette *const palette) {
|
|
|
|
|
if (_varyTargetPalette != nullptr) {
|
|
|
|
|
mergePaletteInternal(_varyTargetPalette, palette);
|
|
|
|
|
} else {
|
|
|
|
|
_varyTargetPalette = new Palette(*palette);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GfxPalette32::varyPause() {
|
|
|
|
|
_varyDirection = 0;
|
|
|
|
|
++_varyNumTimesPaused;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GfxPalette32::varyOn() {
|
|
|
|
|
if (_varyNumTimesPaused > 0) {
|
|
|
|
|
--_varyNumTimesPaused;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (_varyTargetPalette != nullptr && _varyNumTimesPaused == 0 && _varyPercent != _varyTargetPercent) {
|
|
|
|
|
if (_varyTime == 0) {
|
|
|
|
|
_varyPercent = _varyTargetPercent;
|
|
|
|
|
} else if (_varyTargetPercent < _varyPercent) {
|
|
|
|
|
_varyDirection = -1;
|
|
|
|
|
} else {
|
|
|
|
|
_varyDirection = 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GfxPalette32::setVaryTime(const int time) {
|
|
|
|
|
if (_varyTargetPalette == nullptr) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
setVaryTimeInternal(_varyTargetPercent, time);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GfxPalette32::setTarget(const Palette *const palette) {
|
|
|
|
|
if (_varyTargetPalette != nullptr) {
|
|
|
|
|
delete _varyTargetPalette;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_varyTargetPalette = new Palette(*palette);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GfxPalette32::setStart(const Palette *const palette) {
|
|
|
|
|
if (_varyStartPalette != nullptr) {
|
|
|
|
|
delete _varyStartPalette;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_varyStartPalette = new Palette(*palette);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GfxPalette32::mergeStart(const Palette *const palette) {
|
|
|
|
|
if (_varyStartPalette != nullptr) {
|
|
|
|
|
mergePaletteInternal(_varyStartPalette, palette);
|
|
|
|
|
} else {
|
|
|
|
|
_varyStartPalette = new Palette(*palette);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GfxPalette32::applyVary() {
|
2016-01-14 23:53:12 +00:00
|
|
|
|
while (g_sci->getTickCount() - _varyLastTick > (uint32)_varyTime && _varyDirection != 0) {
|
2016-01-14 16:50:41 +00:00
|
|
|
|
_varyLastTick += _varyTime;
|
|
|
|
|
|
|
|
|
|
if (_varyPercent == _varyTargetPercent) {
|
|
|
|
|
_varyDirection = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_varyPercent += _varyDirection;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (_varyPercent == 0 || _varyTargetPalette == nullptr) {
|
|
|
|
|
for (int i = 0, len = ARRAYSIZE(_nextPalette.colors); i < len; ++i) {
|
|
|
|
|
if (_varyStartPalette != nullptr && i >= _varyFromColor && i <= _varyToColor) {
|
|
|
|
|
_nextPalette.colors[i] = _varyStartPalette->colors[i];
|
|
|
|
|
} else {
|
|
|
|
|
_nextPalette.colors[i] = _sourcePalette.colors[i];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
for (int i = 0, len = ARRAYSIZE(_nextPalette.colors); i < len; ++i) {
|
|
|
|
|
if (i >= _varyFromColor && i <= _varyToColor) {
|
|
|
|
|
Color targetColor = _varyTargetPalette->colors[i];
|
|
|
|
|
Color sourceColor;
|
|
|
|
|
|
|
|
|
|
if (_varyStartPalette != nullptr) {
|
|
|
|
|
sourceColor = _varyStartPalette->colors[i];
|
|
|
|
|
} else {
|
|
|
|
|
sourceColor = _sourcePalette.colors[i];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Color computedColor;
|
|
|
|
|
|
|
|
|
|
int color;
|
|
|
|
|
color = targetColor.r - sourceColor.r;
|
|
|
|
|
computedColor.r = ((color * _varyPercent) / 100) + sourceColor.r;
|
|
|
|
|
color = targetColor.g - sourceColor.g;
|
|
|
|
|
computedColor.g = ((color * _varyPercent) / 100) + sourceColor.g;
|
|
|
|
|
color = targetColor.b - sourceColor.b;
|
|
|
|
|
computedColor.b = ((color * _varyPercent) / 100) + sourceColor.b;
|
|
|
|
|
computedColor.used = sourceColor.used;
|
|
|
|
|
|
|
|
|
|
_nextPalette.colors[i] = computedColor;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
_nextPalette.colors[i] = _sourcePalette.colors[i];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-19 03:09:15 +00:00
|
|
|
|
#pragma mark -
|
|
|
|
|
#pragma mark Cycling
|
2016-01-14 16:50:41 +00:00
|
|
|
|
|
2016-01-08 13:40:05 +00:00
|
|
|
|
inline void GfxPalette32::clearCycleMap(const uint16 fromColor, const uint16 numColorsToClear) {
|
2016-01-07 02:00:28 +00:00
|
|
|
|
bool *mapEntry = _cycleMap + fromColor;
|
|
|
|
|
const bool *lastEntry = _cycleMap + numColorsToClear;
|
|
|
|
|
while (mapEntry < lastEntry) {
|
|
|
|
|
*mapEntry++ = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-08 13:40:05 +00:00
|
|
|
|
inline void GfxPalette32::setCycleMap(const uint16 fromColor, const uint16 numColorsToSet) {
|
2016-01-07 02:00:28 +00:00
|
|
|
|
bool *mapEntry = _cycleMap + fromColor;
|
|
|
|
|
const bool *lastEntry = _cycleMap + numColorsToSet;
|
|
|
|
|
while (mapEntry < lastEntry) {
|
|
|
|
|
if (*mapEntry != false) {
|
|
|
|
|
error("Cycles intersect");
|
|
|
|
|
}
|
|
|
|
|
*mapEntry++ = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-08 13:40:05 +00:00
|
|
|
|
inline PalCycler *GfxPalette32::getCycler(const uint16 fromColor) {
|
2016-01-07 02:00:28 +00:00
|
|
|
|
const int numCyclers = ARRAYSIZE(_cyclers);
|
|
|
|
|
|
|
|
|
|
for (int cyclerIndex = 0; cyclerIndex < numCyclers; ++cyclerIndex) {
|
|
|
|
|
PalCycler *cycler = _cyclers[cyclerIndex];
|
|
|
|
|
if (cycler != nullptr && cycler->fromColor == fromColor) {
|
|
|
|
|
return cycler;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-08 13:43:59 +00:00
|
|
|
|
inline void doCycleInternal(PalCycler *cycler, const int16 speed) {
|
2016-01-07 02:00:28 +00:00
|
|
|
|
int16 currentCycle = cycler->currentCycle;
|
|
|
|
|
const uint16 numColorsToCycle = cycler->numColorsToCycle;
|
|
|
|
|
|
|
|
|
|
if (cycler->direction == 0) {
|
|
|
|
|
currentCycle = (currentCycle - (speed % numColorsToCycle)) + numColorsToCycle;
|
|
|
|
|
} else {
|
|
|
|
|
currentCycle = currentCycle + speed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cycler->currentCycle = (uint8) (currentCycle % numColorsToCycle);
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-14 16:50:41 +00:00
|
|
|
|
void GfxPalette32::setCycle(const uint8 fromColor, const uint8 toColor, const int16 direction, const int16 delay) {
|
2016-01-07 02:00:28 +00:00
|
|
|
|
assert(fromColor < toColor);
|
|
|
|
|
|
|
|
|
|
int cyclerIndex;
|
|
|
|
|
const int numCyclers = ARRAYSIZE(_cyclers);
|
|
|
|
|
|
2016-01-08 13:40:05 +00:00
|
|
|
|
PalCycler *cycler = getCycler(fromColor);
|
2016-01-07 02:00:28 +00:00
|
|
|
|
|
|
|
|
|
if (cycler != nullptr) {
|
2016-01-08 13:40:05 +00:00
|
|
|
|
clearCycleMap(fromColor, cycler->numColorsToCycle);
|
2016-01-07 02:00:28 +00:00
|
|
|
|
} else {
|
|
|
|
|
for (cyclerIndex = 0; cyclerIndex < numCyclers; ++cyclerIndex) {
|
|
|
|
|
if (_cyclers[cyclerIndex] == nullptr) {
|
|
|
|
|
cycler = new PalCycler;
|
|
|
|
|
_cyclers[cyclerIndex] = cycler;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SCI engine overrides the first oldest cycler that it finds where
|
|
|
|
|
// “oldest” is determined by the difference between the tick and now
|
|
|
|
|
if (cycler == nullptr) {
|
|
|
|
|
int maxUpdateDelta = -1;
|
|
|
|
|
// Optimization: Unlike actual SCI (SQ6) engine, we call
|
|
|
|
|
// getTickCount only once and store it, instead of calling it
|
|
|
|
|
// twice on each iteration through the loop
|
|
|
|
|
const uint32 now = g_sci->getTickCount();
|
|
|
|
|
|
|
|
|
|
for (cyclerIndex = 0; cyclerIndex < numCyclers; ++cyclerIndex) {
|
|
|
|
|
PalCycler *candidate = _cyclers[cyclerIndex];
|
|
|
|
|
|
|
|
|
|
const int32 updateDelta = now - candidate->lastUpdateTick;
|
|
|
|
|
if (updateDelta >= maxUpdateDelta) {
|
|
|
|
|
maxUpdateDelta = updateDelta;
|
|
|
|
|
cycler = candidate;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-08 13:40:05 +00:00
|
|
|
|
clearCycleMap(cycler->fromColor, cycler->numColorsToCycle);
|
2016-01-07 02:00:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const uint16 numColorsToCycle = 1 + ((uint8) toColor) - fromColor;
|
|
|
|
|
cycler->fromColor = (uint8) fromColor;
|
|
|
|
|
cycler->numColorsToCycle = (uint8) numColorsToCycle;
|
|
|
|
|
cycler->currentCycle = (uint8) fromColor;
|
|
|
|
|
cycler->direction = direction < 0 ? PalCycleBackward : PalCycleForward;
|
|
|
|
|
cycler->delay = delay;
|
|
|
|
|
cycler->lastUpdateTick = g_sci->getTickCount();
|
|
|
|
|
cycler->numTimesPaused = 0;
|
|
|
|
|
|
2016-01-08 13:40:05 +00:00
|
|
|
|
setCycleMap(fromColor, numColorsToCycle);
|
2016-01-07 02:00:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-01-14 16:50:41 +00:00
|
|
|
|
void GfxPalette32::doCycle(const uint8 fromColor, const int16 speed) {
|
2016-01-08 13:40:05 +00:00
|
|
|
|
PalCycler *cycler = getCycler(fromColor);
|
2016-01-07 02:00:28 +00:00
|
|
|
|
if (cycler != nullptr) {
|
|
|
|
|
cycler->lastUpdateTick = g_sci->getTickCount();
|
2016-01-08 13:43:59 +00:00
|
|
|
|
doCycleInternal(cycler, speed);
|
2016-01-07 02:00:28 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-14 16:50:41 +00:00
|
|
|
|
void GfxPalette32::cycleOn(const uint8 fromColor) {
|
2016-01-08 13:40:05 +00:00
|
|
|
|
PalCycler *cycler = getCycler(fromColor);
|
2016-01-07 02:00:28 +00:00
|
|
|
|
if (cycler != nullptr && cycler->numTimesPaused > 0) {
|
|
|
|
|
--cycler->numTimesPaused;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-14 16:50:41 +00:00
|
|
|
|
void GfxPalette32::cyclePause(const uint8 fromColor) {
|
2016-01-08 13:40:05 +00:00
|
|
|
|
PalCycler *cycler = getCycler(fromColor);
|
2016-01-07 02:00:28 +00:00
|
|
|
|
if (cycler != nullptr) {
|
|
|
|
|
++cycler->numTimesPaused;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GfxPalette32::cycleAllOn() {
|
|
|
|
|
for (int i = 0, len = ARRAYSIZE(_cyclers); i < len; ++i) {
|
|
|
|
|
PalCycler *cycler = _cyclers[i];
|
|
|
|
|
if (cycler != nullptr && cycler->numTimesPaused > 0) {
|
|
|
|
|
--cycler->numTimesPaused;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GfxPalette32::cycleAllPause() {
|
2016-03-08 04:51:37 +00:00
|
|
|
|
// NOTE: The original engine did not check for null pointers in the
|
|
|
|
|
// palette cyclers pointer array.
|
2016-01-07 02:00:28 +00:00
|
|
|
|
for (int i = 0, len = ARRAYSIZE(_cyclers); i < len; ++i) {
|
|
|
|
|
PalCycler *cycler = _cyclers[i];
|
|
|
|
|
if (cycler != nullptr) {
|
|
|
|
|
// This seems odd, because currentCycle is 0..numColorsPerCycle,
|
|
|
|
|
// but fromColor is 0..255. When applyAllCycles runs, the values
|
|
|
|
|
// end up back in range
|
|
|
|
|
cycler->currentCycle = cycler->fromColor;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
applyAllCycles();
|
|
|
|
|
|
|
|
|
|
for (int i = 0, len = ARRAYSIZE(_cyclers); i < len; ++i) {
|
|
|
|
|
PalCycler *cycler = _cyclers[i];
|
|
|
|
|
if (cycler != nullptr) {
|
|
|
|
|
++cycler->numTimesPaused;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-14 16:50:41 +00:00
|
|
|
|
void GfxPalette32::cycleOff(const uint8 fromColor) {
|
2016-01-07 02:00:28 +00:00
|
|
|
|
for (int i = 0, len = ARRAYSIZE(_cyclers); i < len; ++i) {
|
|
|
|
|
PalCycler *cycler = _cyclers[i];
|
|
|
|
|
if (cycler != nullptr && cycler->fromColor == fromColor) {
|
2016-01-08 13:40:05 +00:00
|
|
|
|
clearCycleMap(fromColor, cycler->numColorsToCycle);
|
2016-01-07 02:00:28 +00:00
|
|
|
|
delete cycler;
|
|
|
|
|
_cyclers[i] = nullptr;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GfxPalette32::cycleAllOff() {
|
|
|
|
|
for (int i = 0, len = ARRAYSIZE(_cyclers); i < len; ++i) {
|
|
|
|
|
PalCycler *cycler = _cyclers[i];
|
|
|
|
|
if (cycler != nullptr) {
|
2016-01-08 13:40:05 +00:00
|
|
|
|
clearCycleMap(cycler->fromColor, cycler->numColorsToCycle);
|
2016-01-07 02:00:28 +00:00
|
|
|
|
delete cycler;
|
|
|
|
|
_cyclers[i] = nullptr;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-14 16:50:41 +00:00
|
|
|
|
void GfxPalette32::applyAllCycles() {
|
|
|
|
|
Color paletteCopy[256];
|
|
|
|
|
memcpy(paletteCopy, _nextPalette.colors, sizeof(Color) * 256);
|
|
|
|
|
|
|
|
|
|
for (int cyclerIndex = 0, numCyclers = ARRAYSIZE(_cyclers); cyclerIndex < numCyclers; ++cyclerIndex) {
|
|
|
|
|
PalCycler *cycler = _cyclers[cyclerIndex];
|
|
|
|
|
if (cycler != nullptr) {
|
|
|
|
|
cycler->currentCycle = (uint8) ((((int) cycler->currentCycle) + 1) % cycler->numColorsToCycle);
|
|
|
|
|
// Disassembly was not fully evaluated to verify this is exactly the same
|
|
|
|
|
// as the code from applyCycles, but it appeared to be at a glance
|
|
|
|
|
for (int j = 0; j < cycler->numColorsToCycle; j++) {
|
|
|
|
|
_nextPalette.colors[cycler->fromColor + j] = paletteCopy[cycler->fromColor + (cycler->currentCycle + j) % cycler->numColorsToCycle];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GfxPalette32::applyCycles() {
|
|
|
|
|
Color paletteCopy[256];
|
|
|
|
|
memcpy(paletteCopy, _nextPalette.colors, sizeof(Color) * 256);
|
|
|
|
|
|
|
|
|
|
for (int i = 0, len = ARRAYSIZE(_cyclers); i < len; ++i) {
|
|
|
|
|
PalCycler *cycler = _cyclers[i];
|
|
|
|
|
if (cycler == nullptr) {
|
2016-01-07 02:00:28 +00:00
|
|
|
|
continue;
|
2016-01-14 16:50:41 +00:00
|
|
|
|
}
|
2016-01-07 02:00:28 +00:00
|
|
|
|
|
2016-01-14 16:50:41 +00:00
|
|
|
|
if (cycler->delay != 0 && cycler->numTimesPaused == 0) {
|
|
|
|
|
while ((cycler->delay + cycler->lastUpdateTick) < g_sci->getTickCount()) {
|
|
|
|
|
doCycleInternal(cycler, 1);
|
|
|
|
|
cycler->lastUpdateTick += cycler->delay;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int j = 0; j < cycler->numColorsToCycle; j++) {
|
|
|
|
|
_nextPalette.colors[cycler->fromColor + j] = paletteCopy[cycler->fromColor + (cycler->currentCycle + j) % cycler->numColorsToCycle];
|
|
|
|
|
}
|
2016-01-07 02:00:28 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-19 03:09:15 +00:00
|
|
|
|
#pragma mark -
|
|
|
|
|
#pragma mark Fading
|
2016-01-14 16:50:41 +00:00
|
|
|
|
|
2016-01-18 06:12:47 +00:00
|
|
|
|
// NOTE: There are some game scripts (like SQ6 Sierra logo and main menu) that call
|
|
|
|
|
// setFade with numColorsToFade set to 256, but other parts of the engine like
|
|
|
|
|
// processShowStyleNone use 255 instead of 256. It is not clear if this is because
|
|
|
|
|
// the last palette entry is intentionally left unmodified, or if this is a bug
|
|
|
|
|
// in the engine. It certainly seems confused because all other places that accept
|
2016-02-19 00:50:16 +00:00
|
|
|
|
// color ranges typically receive values in the range of 0–255.
|
2016-03-08 05:47:44 +00:00
|
|
|
|
void GfxPalette32::setFade(uint16 percent, uint8 fromColor, uint16 numColorsToFade) {
|
2016-01-14 16:50:41 +00:00
|
|
|
|
if (fromColor > numColorsToFade) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
assert(numColorsToFade <= ARRAYSIZE(_fadeTable));
|
|
|
|
|
|
|
|
|
|
for (int i = fromColor; i < numColorsToFade; i++)
|
2016-01-08 15:16:09 +00:00
|
|
|
|
_fadeTable[i] = percent;
|
2016-01-07 02:00:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GfxPalette32::fadeOff() {
|
2016-01-14 16:50:41 +00:00
|
|
|
|
setFade(100, 0, 256);
|
2016-01-07 02:00:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-01-14 16:50:41 +00:00
|
|
|
|
void GfxPalette32::applyFade() {
|
|
|
|
|
for (int i = 0; i < ARRAYSIZE(_fadeTable); ++i) {
|
|
|
|
|
if (_fadeTable[i] == 100)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
Color &color = _nextPalette.colors[i];
|
|
|
|
|
|
2016-03-08 05:47:44 +00:00
|
|
|
|
color.r = MIN(255, (uint16)color.r * _fadeTable[i] / 100);
|
|
|
|
|
color.g = MIN(255, (uint16)color.g * _fadeTable[i] / 100);
|
|
|
|
|
color.b = MIN(255, (uint16)color.b * _fadeTable[i] / 100);
|
2016-01-14 16:50:41 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2016-03-08 18:30:55 +00:00
|
|
|
|
|
2016-03-08 19:13:05 +00:00
|
|
|
|
} // End of namespace Sci
|