diff options
Diffstat (limited to 'engines/sci/graphics/palette.cpp')
-rw-r--r-- | engines/sci/graphics/palette.cpp | 380 |
1 files changed, 380 insertions, 0 deletions
diff --git a/engines/sci/graphics/palette.cpp b/engines/sci/graphics/palette.cpp new file mode 100644 index 0000000000..ed2fa0c003 --- /dev/null +++ b/engines/sci/graphics/palette.cpp @@ -0,0 +1,380 @@ +/* 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. + * + * $URL$ + * $Id$ + * + */ + +#include "common/timer.h" +#include "common/util.h" + +#include "sci/sci.h" +#include "sci/engine/state.h" +#include "sci/graphics/screen.h" +#include "sci/graphics/palette.h" + +namespace Sci { + +SciPalette::SciPalette(ResourceManager *resMan, Screen *screen, bool autoSetPalette) + : _resMan(resMan), _screen(screen) { + int16 color; + + _sysPalette.timestamp = 0; + for (color = 0; color < 256; color++) { + _sysPalette.colors[color].used = 0; + _sysPalette.colors[color].r = 0; + _sysPalette.colors[color].g = 0; + _sysPalette.colors[color].b = 0; + _sysPalette.intensity[color] = 100; + _sysPalette.mapping[color] = color; + } + // Black and white are hardcoded + _sysPalette.colors[0].used = 1; + _sysPalette.colors[255].used = 1; + _sysPalette.colors[255].r = 255; + _sysPalette.colors[255].g = 255; + _sysPalette.colors[255].b = 255; + + if (autoSetPalette) { + if (_resMan->getViewType() == kViewEga) + setEGA(); + else if (_resMan->getViewType() == kViewAmiga) + setAmiga(); + else + setFromResource(999, 2); + } +} + +SciPalette::~SciPalette() { +} + +#define SCI_PAL_FORMAT_CONSTANT 1 +#define SCI_PAL_FORMAT_VARIABLE 0 + +void SciPalette::createFromData(byte *data, Palette *paletteOut) { + int palFormat = 0; + int palOffset = 0; + int palColorStart = 0; + int palColorCount = 0; + int colorNo = 0; + + memset(paletteOut, 0, sizeof(Palette)); + // Setup default mapping + for (colorNo = 0; colorNo < 256; colorNo++) { + paletteOut->mapping[colorNo] = colorNo; + } + if ((data[0] == 0 && data[1] == 1) || (data[0] == 0 && data[1] == 0 && READ_LE_UINT16(data + 29) == 0)) { + // SCI0/SCI1 palette + palFormat = SCI_PAL_FORMAT_VARIABLE; // CONSTANT; + palOffset = 260; + palColorStart = 0; palColorCount = 256; + //memcpy(&paletteOut->mapping, data, 256); + } else { + // SCI1.1 palette + palFormat = data[32]; + palOffset = 37; + palColorStart = READ_LE_UINT16(data + 25); palColorCount = READ_LE_UINT16(data + 29); + } + switch (palFormat) { + case SCI_PAL_FORMAT_CONSTANT: + for (colorNo = palColorStart; colorNo < palColorStart + palColorCount; colorNo++) { + paletteOut->colors[colorNo].used = 1; + paletteOut->colors[colorNo].r = data[palOffset++]; + paletteOut->colors[colorNo].g = data[palOffset++]; + paletteOut->colors[colorNo].b = data[palOffset++]; + } + break; + case SCI_PAL_FORMAT_VARIABLE: + for (colorNo = palColorStart; colorNo < palColorStart + palColorCount; colorNo++) { + paletteOut->colors[colorNo].used = data[palOffset++]; + paletteOut->colors[colorNo].r = data[palOffset++]; + paletteOut->colors[colorNo].g = data[palOffset++]; + paletteOut->colors[colorNo].b = data[palOffset++]; + } + break; + } +} + + +// Will try to set amiga palette by using "spal" file. If not found, we return false +bool SciPalette::setAmiga() { + Common::File file; + int curColor, byte1, byte2; + + if (file.open("spal")) { + for (curColor = 0; curColor < 32; curColor++) { + byte1 = file.readByte(); + byte2 = file.readByte(); + if ((byte1 == EOF) || (byte2 == EOF)) + error("Amiga palette file ends prematurely"); + _sysPalette.colors[curColor].used = 1; + _sysPalette.colors[curColor].r = (byte1 & 0x0F) * 0x11; + _sysPalette.colors[curColor].g = ((byte2 & 0xF0) >> 4) * 0x11; + _sysPalette.colors[curColor].b = (byte2 & 0x0F) * 0x11; + } + file.close(); + setOnScreen(); + return true; + } + return false; +} + +void SciPalette::setEGA() { + int i; + byte color1, color2; + _sysPalette.colors[1].r = 0x000; _sysPalette.colors[1].g = 0x000; _sysPalette.colors[1].b = 0x0AA; + _sysPalette.colors[2].r = 0x000; _sysPalette.colors[2].g = 0x0AA; _sysPalette.colors[2].b = 0x000; + _sysPalette.colors[3].r = 0x000; _sysPalette.colors[3].g = 0x0AA; _sysPalette.colors[3].b = 0x0AA; + _sysPalette.colors[4].r = 0x0AA; _sysPalette.colors[4].g = 0x000; _sysPalette.colors[4].b = 0x000; + _sysPalette.colors[5].r = 0x0AA; _sysPalette.colors[5].g = 0x000; _sysPalette.colors[5].b = 0x0AA; + _sysPalette.colors[6].r = 0x0AA; _sysPalette.colors[6].g = 0x055; _sysPalette.colors[6].b = 0x000; + _sysPalette.colors[7].r = 0x0AA; _sysPalette.colors[7].g = 0x0AA; _sysPalette.colors[7].b = 0x0AA; + _sysPalette.colors[8].r = 0x055; _sysPalette.colors[8].g = 0x055; _sysPalette.colors[8].b = 0x055; + _sysPalette.colors[9].r = 0x055; _sysPalette.colors[9].g = 0x055; _sysPalette.colors[9].b = 0x0FF; + _sysPalette.colors[10].r = 0x055; _sysPalette.colors[10].g = 0x0FF; _sysPalette.colors[10].b = 0x055; + _sysPalette.colors[11].r = 0x055; _sysPalette.colors[11].g = 0x0FF; _sysPalette.colors[11].b = 0x0FF; + _sysPalette.colors[12].r = 0x0FF; _sysPalette.colors[12].g = 0x055; _sysPalette.colors[12].b = 0x055; + _sysPalette.colors[13].r = 0x0FF; _sysPalette.colors[13].g = 0x055; _sysPalette.colors[13].b = 0x0FF; + _sysPalette.colors[14].r = 0x0FF; _sysPalette.colors[14].g = 0x0FF; _sysPalette.colors[14].b = 0x055; + _sysPalette.colors[15].r = 0x0FF; _sysPalette.colors[15].g = 0x0FF; _sysPalette.colors[15].b = 0x0FF; + for (i = 0; i <= 15; i++) { + _sysPalette.colors[i].used = 1; + } + // Now setting colors 16-254 to the correct mix colors that occur when not doing a dithering run on + // finished pictures + for (i = 0x10; i <= 0xFE; i++) { + _sysPalette.colors[i].used = 1; + color1 = i & 0x0F; color2 = i >> 4; + _sysPalette.colors[i].r = (_sysPalette.colors[color1].r >> 1) + (_sysPalette.colors[color2].r >> 1); + _sysPalette.colors[i].g = (_sysPalette.colors[color1].g >> 1) + (_sysPalette.colors[color2].g >> 1); + _sysPalette.colors[i].b = (_sysPalette.colors[color1].b >> 1) + (_sysPalette.colors[color2].b >> 1); + } + setOnScreen(); +} + +bool SciPalette::setFromResource(GuiResourceId resourceId, uint16 flag) { + Resource *palResource = _resMan->findResource(ResourceId(kResourceTypePalette, resourceId), 0); + Palette palette; + + if (palResource) { + createFromData(palResource->data, &palette); + set(&palette, 2); + return true; + } + return false; +} + +void SciPalette::set(Palette *sciPal, uint16 flag) { + uint32 systime = _sysPalette.timestamp; + if (flag == 2 || sciPal->timestamp != systime) { + merge(sciPal, &_sysPalette, flag); + sciPal->timestamp = _sysPalette.timestamp; + if (_screen->_picNotValid == 0 && systime != _sysPalette.timestamp) + setOnScreen(); + } +} + +void SciPalette::merge(Palette *pFrom, Palette *pTo, uint16 flag) { + uint16 res; + int i,j; + // colors 0 (black) and 255 (white) are not affected by merging + for (i = 1 ; i < 255; i++) { + if (!pFrom->colors[i].used)// color is not used - so skip it + continue; + // forced palette merging or dest color is not used yet + if (flag == 2 || (!pTo->colors[i].used)) { + pTo->colors[i].used = pFrom->colors[i].used; + pTo->colors[i].r = pFrom->colors[i].r; + pTo->colors[i].g = pFrom->colors[i].g; + pTo->colors[i].b = pFrom->colors[i].b; + pFrom->mapping[i] = i; + continue; + } + // is the same color already at the same position? -> match it directly w/o lookup + // this fixes games like lsl1demo/sq5 where the same rgb color exists multiple times and where we would + // otherwise match the wrong one (which would result into the pixels affected (or not) by palette changes) + if ((pTo->colors[i].r == pFrom->colors[i].r) && (pTo->colors[i].g == pFrom->colors[i].g) && (pTo->colors[i].b == pFrom->colors[i].b)) { + pFrom->mapping[i] = i; + continue; + } + // check if exact color could be matched + res = matchColor(pTo, pFrom->colors[i].r, pFrom->colors[i].g, pFrom->colors[i].b); + if (res & 0x8000) { // exact match was found + pFrom->mapping[i] = res & 0xFF; + continue; + } + // no exact match - see if there is an unused color + for (j = 1; j < 256; j++) + if (!pTo->colors[j].used) { + pTo->colors[j].used = pFrom->colors[i].used; + pTo->colors[j].r = pFrom->colors[i].r; + pTo->colors[j].g = pFrom->colors[i].g; + pTo->colors[j].b = pFrom->colors[i].b; + pFrom->mapping[i] = j; + break; + } + // if still no luck - set an approximate color + if (j == 256) { + pFrom->mapping[i] = res & 0xFF; + pTo->colors[res & 0xFF].used |= 0x10; + } + } + pTo->timestamp = g_system->getMillis() * 60 / 1000; +} + +uint16 SciPalette::matchColor(Palette *pPal, byte r, byte g, byte b) { + byte found = 0xFF; + int diff = 0x2FFFF, cdiff; + int16 dr,dg,db; + + for (int i = 1; i < 255; i++) { + if ((!pPal->colors[i].used)) + continue; + dr = pPal->colors[i].r - r; + dg = pPal->colors[i].g - g; + db = pPal->colors[i].b - b; +// minimum squares match + cdiff = (dr*dr) + (dg*dg) + (db*db); +// minimum sum match (Sierra's) +// cdiff = ABS(dr) + ABS(dg) + ABS(db); + if (cdiff < diff) { + if (cdiff == 0) + return i | 0x8000; // setting this flag to indicate exact match + found = i; + diff = cdiff; + } + } + return found; +} + +void SciPalette::getSys(Palette *pal) { + if (pal != &_sysPalette) + memcpy(pal, &_sysPalette,sizeof(Palette)); +} + +void SciPalette::setOnScreen() { +// if (pal != &_sysPalette) +// memcpy(&_sysPalette,pal,sizeof(Palette)); + _screen->setPalette(&_sysPalette); +} + +void SciPalette::setFlag(uint16 fromColor, uint16 toColor, uint16 flag) { + uint16 colorNr; + for (colorNr = fromColor; colorNr < toColor; colorNr++) { + _sysPalette.colors[colorNr].used |= flag; + } +} + +void SciPalette::unsetFlag(uint16 fromColor, uint16 toColor, uint16 flag) { + uint16 colorNr; + for (colorNr = fromColor; colorNr < toColor; colorNr++) { + _sysPalette.colors[colorNr].used &= ~flag; + } +} + +void SciPalette::setIntensity(uint16 fromColor, uint16 toColor, uint16 intensity, bool setPalette) { + memset(&_sysPalette.intensity[0] + fromColor, intensity, toColor - fromColor); + if (setPalette) + setOnScreen(); +} + +// Returns true, if palette got changed +bool SciPalette::animate(byte fromColor, byte toColor, int speed) { + Color col; + //byte colorNr; + int16 colorCount; + uint32 now = g_system->getMillis() * 60 / 1000; + + // search for sheduled animations with the same 'from' value + // schedule animation... + int scheduleCount = _schedules.size(); + int scheduleNr; + for (scheduleNr = 0; scheduleNr < scheduleCount; scheduleNr++) { + if (_schedules[scheduleNr].from == fromColor) + break; + } + if (scheduleNr == scheduleCount) { + // adding a new schedule + PalSchedule newSchedule; + newSchedule.from = fromColor; + newSchedule.schedule = now + ABS(speed); + _schedules.push_back(newSchedule); + scheduleCount++; + } + + for (scheduleNr = 0; scheduleNr < scheduleCount; scheduleNr++) { + if (_schedules[scheduleNr].from == fromColor) { + if (_schedules[scheduleNr].schedule <= now) { + if (speed > 0) { + // TODO: Not really sure about this, sierra sci seems to do exactly this here + col = _sysPalette.colors[fromColor]; + if (fromColor < toColor) { + colorCount = toColor - fromColor - 1; + memmove(&_sysPalette.colors[fromColor], &_sysPalette.colors[fromColor + 1], colorCount * sizeof(Color)); + } + _sysPalette.colors[toColor - 1] = col; + } else { + col = _sysPalette.colors[toColor - 1]; + if (fromColor < toColor) { + colorCount = toColor - fromColor - 1; + memmove(&_sysPalette.colors[fromColor + 1], &_sysPalette.colors[fromColor], colorCount * sizeof(Color)); + } + _sysPalette.colors[fromColor] = col; + } + // removing schedule + _schedules[scheduleNr].schedule = now + ABS(speed); + // TODO: Not sure when sierra actually removes a schedule + //_schedules.remove_at(scheduleNr); + return true; + } + return false; + } + } + return false; +} + +// palVary +// init - only does, if palVaryOn == false +// target, start, new palette allocation +// palVaryOn = true +// palDirection = 1 +// palStop = 64 +// palTime = from caller +// copy resource palette to target +// init target palette (used = 1 on all colors, color 0 = RGB 0, 0, 0 color 255 = RGB 0xFF, 0xFF, 0xFF +// copy sysPalette to startPalette +// init new palette like target palette +// palPercent = 1 +// do various things +// return 1 +// deinit - unloads target palette, kills timer hook, disables palVaryOn +// pause - counts up or down, if counter != 0 -> signal wont get counted up by timer +// will only count down to 0 +// +// Restarting game +// palVary = false +// palPercent = 0 +// call palVary deinit +// +// Saving/restoring +// need to save start and target-palette, when palVaryOn = true + +} // End of namespace Sci |