aboutsummaryrefslogtreecommitdiff
path: root/engines/mads/palette.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/mads/palette.cpp')
-rw-r--r--engines/mads/palette.cpp447
1 files changed, 447 insertions, 0 deletions
diff --git a/engines/mads/palette.cpp b/engines/mads/palette.cpp
new file mode 100644
index 0000000000..aca1298a23
--- /dev/null
+++ b/engines/mads/palette.cpp
@@ -0,0 +1,447 @@
+/* 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/scummsys.h"
+#include "engines/util.h"
+#include "graphics/palette.h"
+#include "mads/mads.h"
+#include "mads/msurface.h"
+
+namespace MADS {
+
+void RGB6::load(Common::SeekableReadStream *f) {
+ r = f->readByte();
+ g = f->readByte();
+ b = f->readByte();
+ palIndex = f->readByte();
+ u2 = f->readByte();
+ flags = f->readByte();
+}
+
+RGBList::RGBList(int numEntries, byte *srcData, bool freeData) {
+ _size = numEntries;
+ assert(numEntries <= PALETTE_COUNT);
+
+ if (srcData == NULL) {
+ _data = new byte[numEntries * 3];
+ _freeData = true;
+ } else {
+ _data = srcData;
+ _freeData = freeData;
+ }
+
+ _palIndexes = new byte[numEntries];
+ Common::fill(&_palIndexes[0], &_palIndexes[numEntries], 0);
+}
+
+RGBList::~RGBList() {
+ if (_freeData)
+ delete[] _data;
+ delete[] _palIndexes;
+}
+
+/*------------------------------------------------------------------------*/
+
+PaletteUsage::PaletteUsage() {
+}
+
+void PaletteUsage::load(int count, ...) {
+ va_list va;
+ va_start(va, count);
+
+ _data.clear();
+ for (int i = 0; i < count; ++i)
+ _data.push_back(va_arg(va, int));
+
+ va_end(va);
+}
+
+
+void PaletteUsage::getKeyEntries(Common::Array<RGB6> &palette) {
+ _data.clear();
+
+ for (uint i = 0; i < palette.size(); ++i) {
+ byte *uPtr = &palette[i].flags;
+ if ((*uPtr & 0x10) && _data.size() < 3) {
+ _data.push_back(i);
+ }
+ }
+}
+
+void PaletteUsage::prioritize(Common::Array<RGB6> &palette) {
+ int lst[3];
+
+ for (uint i = 0; i < _data.size(); ++i) {
+ RGB6 &palEntry = palette[_data[i]];
+ lst[i] = rgbMerge(palEntry);
+ }
+
+ prioritizeFromList(lst);
+}
+
+int PaletteUsage::rgbMerge(RGB6 &palEntry) {
+ return palEntry.r * 38 + palEntry.g * 76 + palEntry.b * 14;
+}
+
+void PaletteUsage::prioritizeFromList(int lst[3]) {
+ int idx1 = _data.size() - 1;
+ bool continueFlag;
+ int count2;
+
+ do {
+ continueFlag = false;
+ count2 = 0;
+
+ if (idx1 > 0) {
+ int numEntries = _data.size() - 1;
+ int usageIndex = 0, lstIndex = 0;
+
+ do {
+ if (lst[lstIndex] < lst[lstIndex + 1]) {
+ int lstVal = lst[lstIndex];
+ int usageVal = _data[usageIndex];
+
+ if (numEntries > 0) {
+ Common::copy(&lst[lstIndex + 1], &lst[lstIndex + numEntries], &lst[lstIndex]);
+ _data.remove_at(usageIndex);
+ _data.push_back(0);
+ }
+
+ int newIdx = 0;
+ if (idx1 > 0 && !continueFlag) {
+ for (newIdx = 0; newIdx <= idx1; ++newIdx) {
+ if (lst[newIdx] > lstVal)
+ break;
+ }
+ }
+
+ continueFlag = true;
+ int idxDiff = _data.size() - newIdx - 1;
+ if (idxDiff > 0) {
+ Common::copy_backward(&lst[0], &lst[2], &lst[1]);
+ _data.remove_at(2);
+ _data.insert_at(0, 0);
+ }
+
+ lst[newIdx] = lstVal;
+ _data[newIdx] = usageVal;
+ }
+
+ ++usageIndex;
+ --numEntries;
+ ++lstIndex;
+ ++count2;
+ } while (count2 > idx1 && !continueFlag);
+ }
+ } while (continueFlag);
+}
+
+void PaletteUsage::transform(Common::Array<RGB6> &palette) {
+ if (!empty()) {
+ for (uint i = 0; i < _data.size(); ++i) {
+ int palIndex = _data[i];
+ _data[i] = palette[palIndex].palIndex;
+ }
+ }
+}
+
+/*------------------------------------------------------------------------*/
+
+#define VGA_COLOR_TRANS(x) (x == 0x3f ? 255 : x << 2)
+
+Palette::Palette(MADSEngine *vm) : _vm(vm) {
+ reset();
+ _fading_in_progress = false;
+ Common::fill(&_usageCount[0], &_usageCount[PALETTE_COUNT], 0);
+ Common::fill(&_mainPalette[0], &_mainPalette[PALETTE_SIZE], 0);
+}
+
+void Palette::setPalette(const byte *colors, uint start, uint num) {
+ g_system->getPaletteManager()->setPalette(colors, start, num);
+ reset();
+}
+
+void Palette::grabPalette(byte *colors, uint start, uint num) {
+ g_system->getPaletteManager()->grabPalette(colors, start, num);
+ reset();
+}
+
+uint8 Palette::palIndexFromRgb(byte r, byte g, byte b, byte *paletteData) {
+ byte index = 0;
+ int32 minDist = 0x7fffffff;
+ byte palData[PALETTE_SIZE];
+ int Rdiff, Gdiff, Bdiff;
+
+ if (paletteData == NULL) {
+ g_system->getPaletteManager()->grabPalette(palData, 0, PALETTE_COUNT);
+ paletteData = &palData[0];
+ }
+
+ for (int palIndex = 0; palIndex < PALETTE_COUNT; ++palIndex) {
+ Rdiff = r - paletteData[palIndex * 3];
+ Gdiff = g - paletteData[palIndex * 3 + 1];
+ Bdiff = b - paletteData[palIndex * 3 + 2];
+
+ if (Rdiff * Rdiff + Gdiff * Gdiff + Bdiff * Bdiff < minDist) {
+ minDist = Rdiff * Rdiff + Gdiff * Gdiff + Bdiff * Bdiff;
+ index = (uint8)palIndex;
+ }
+ }
+
+ return (uint8)index;
+}
+
+void Palette::reset() {
+ byte palData[PALETTE_SIZE];
+ g_system->getPaletteManager()->grabPalette(palData, 0, PALETTE_COUNT);
+
+ BLACK = palIndexFromRgb(0, 0, 0, palData);
+ BLUE = palIndexFromRgb(0, 0, 255, palData);
+ GREEN = palIndexFromRgb(0, 255, 0, palData);
+ CYAN = palIndexFromRgb(0, 255, 255, palData);
+ RED = palIndexFromRgb(255, 0, 0, palData);
+ VIOLET = palIndexFromRgb(255, 0, 255, palData);
+ BROWN = palIndexFromRgb(168, 84, 84, palData);
+ LIGHT_GRAY = palIndexFromRgb(168, 168, 168, palData);
+ DARK_GRAY = palIndexFromRgb(84, 84, 84, palData);
+ LIGHT_BLUE = palIndexFromRgb(0, 0, 127, palData);
+ LIGHT_GREEN = palIndexFromRgb(0, 127, 0, palData);
+ LIGHT_CYAN = palIndexFromRgb(0, 127, 127, palData);
+ LIGHT_RED = palIndexFromRgb(84, 0, 0, palData);
+ PINK = palIndexFromRgb(84, 0, 0, palData);
+ YELLOW = palIndexFromRgb(0, 84, 84, palData);
+ WHITE = palIndexFromRgb(255, 255, 255, palData);
+}
+
+void Palette::resetColorCounts() {
+ Common::fill(&_usageCount[0], &_usageCount[PALETTE_COUNT], 0);
+}
+
+void Palette::blockRange(int startIndex, int size) {
+ // Use a reference count of -1 to signal a palette index shouldn't be used
+ Common::fill(&_usageCount[startIndex], &_usageCount[startIndex + size], -1);
+}
+
+void Palette::addRange(RGBList *list) {
+ byte *data = list->data();
+ byte *palIndexes = list->palIndexes();
+ byte palData[PALETTE_COUNT];
+ g_system->getPaletteManager()->grabPalette(palData, 0, PALETTE_COUNT);
+ bool paletteChanged = false;
+
+ for (int colIndex = 0; colIndex < list->size(); ++colIndex) {
+ // Scan through for an existing copy of the RGB value
+ int palIndex = -1;
+ while (++palIndex < PALETTE_COUNT) {
+ if (_usageCount[palIndex] <= 0)
+ // Palette index is to be skipped
+ continue;
+
+ if ((palData[palIndex * 3] == data[colIndex * 3]) &&
+ (palData[palIndex * 3 + 1] == data[colIndex * 3 + 1]) &&
+ (palData[palIndex * 3 + 2] == data[colIndex * 3 + 2]))
+ // Match found
+ break;
+ }
+
+ if (palIndex == PALETTE_COUNT) {
+ // No match found, so find a free slot to use
+ palIndex = -1;
+ while (++palIndex < PALETTE_COUNT) {
+ if (_usageCount[palIndex] == 0)
+ break;
+ }
+
+ if (palIndex == PALETTE_COUNT)
+ error("addRange - Ran out of palette space to allocate");
+
+ palData[palIndex * 3] = data[colIndex * 3];
+ palData[palIndex * 3 + 1] = data[colIndex * 3 + 1];
+ palData[palIndex * 3 + 2] = data[colIndex * 3 + 2];
+ paletteChanged = true;
+ }
+
+ palIndexes[colIndex] = palIndex;
+ ++_usageCount[palIndex];
+ }
+
+ if (paletteChanged) {
+ g_system->getPaletteManager()->setPalette(&palData[0], 0, 256);
+ reset();
+ }
+}
+
+void Palette::deleteRange(RGBList *list) {
+ // Release the reference count on each of the palette entries
+ for (int colIndex = 0; colIndex < list->size(); ++colIndex) {
+ int palIndex = list->palIndexes()[colIndex];
+ assert(_usageCount[palIndex] > 0);
+ --_usageCount[palIndex];
+ }
+}
+
+void Palette::deleteAllRanges() {
+ for (int colIndex = 0; colIndex < 255; ++colIndex)
+ _usageCount[colIndex] = 0;
+}
+
+void Palette::setGradient(byte *palette, int start, int count, int rgbValue1, int rgbValue2) {
+ int rgbCtr = 0;
+ int rgbCurrent = rgbValue2;
+ int rgbDiff = -(rgbValue2 - rgbValue1);
+
+ if (count > 0) {
+ byte *pDest = palette + start * 3;
+ int endVal = count - 1;
+ int numLeft = count;
+
+ do {
+ pDest[0] = pDest[1] = pDest[2] = rgbCurrent;
+
+ if (numLeft > 1) {
+ rgbCtr += rgbDiff;
+ if (rgbCtr >= endVal) {
+ do {
+ ++rgbCurrent;
+ rgbCtr += 1 - numLeft;
+ } while (rgbCtr >= endVal);
+ }
+ }
+
+ pDest += 3;
+ } while (--numLeft > 0);
+ }
+}
+
+byte *Palette::decodePalette(Common::SeekableReadStream *palStream, int *numColors) {
+ *numColors = palStream->readUint16LE();
+ assert(*numColors <= 252);
+
+ byte *palData = new byte[*numColors * 3];
+ Common::fill(&palData[0], &palData[*numColors * 3], 0);
+
+ for (int i = 0; i < *numColors; ++i) {
+ byte r = palStream->readByte();
+ byte g = palStream->readByte();
+ byte b = palStream->readByte();
+ palData[i * 3] = VGA_COLOR_TRANS(r);
+ palData[i * 3 + 1] = VGA_COLOR_TRANS(g);
+ palData[i * 3 + 2] = VGA_COLOR_TRANS(b);
+
+ // The next 3 bytes are unused
+ palStream->skip(3);
+ }
+
+ return palData;
+}
+
+int Palette::loadPalette(Common::SeekableReadStream *palStream, int indexStart) {
+ int colorCount;
+ byte *palData = decodePalette(palStream, &colorCount);
+ _vm->_palette->setPalette(palData, indexStart, colorCount);
+
+ delete palData;
+ return colorCount;
+}
+
+void Palette::setSystemPalette() {
+ resetColorCounts();
+
+ byte palData[4 * 3];
+ palData[0 * 3] = palData[0 * 3 + 1] = palData[0 * 3 + 2] = 0;
+ palData[1 * 3] = palData[1 * 3 + 1] = palData[1 * 3 + 2] = 0x54;
+ palData[2 * 3] = palData[2 * 3 + 1] = palData[2 * 3 + 2] = 0xb4;
+ palData[3 * 3] = palData[3 * 3 + 1] = palData[3 * 3 + 2] = 0xff;
+
+ setPalette(palData, 0, 4);
+ blockRange(0, 4);
+}
+
+void Palette::resetGamePalette(int lowRange, int highRange) {
+ Common::fill((byte *)&_gamePalette[0], (byte *)&_gamePalette[PALETTE_COUNT], 0);
+ initRange(_mainPalette);
+
+ // Init low range to common RGB values
+ if (lowRange) {
+ _gamePalette[0].r = 1;
+ _gamePalette[0].b = 0;
+
+ Common::fill(&_gamePalette[1], &_gamePalette[lowRange - 1], _gamePalette[0]);
+ }
+
+ // Init high range to common RGB values
+ if (highRange) {
+ _gamePalette[255].r = 1;
+ _gamePalette[255].b = 0;
+
+ Common::fill(&_gamePalette[255 - highRange], &_gamePalette[254], _gamePalette[255]);
+ }
+}
+
+void Palette::initGamePalette() {
+ // TODO
+}
+
+void Palette::initRange(byte *palette) {
+ int var6 = 0;
+ int vdx = 0;
+ int vbx = 0;
+ do {
+ int vdi = (vdx == 1) ? 0 : 0x2A;
+ int var8 = 0;
+ int varE = vbx;
+ int var10 = vdx;
+ do {
+ vdx = 0;
+ do {
+ int vcx = 0;
+ int var4 = vdx;
+ do {
+ int var2 = var6 + vcx;
+ byte *destP = &palette[var2 * 3];
+
+ destP[0] = (var8) ? vdi & 0xFF : vbx & 0XFF;
+ destP[1] = (var4) ? vdi & 0xFF : vbx & 0XFF;
+ destP[2] = (vcx) ? vdi & 0xFF : vbx & 0XFF;
+ } while (++vcx < 2);
+
+ var6 += 2;
+ vdx = var4;
+ } while (++vdx < 2);
+ } while (++var8 < 2);
+
+ vdx = var10 + 1;
+ vbx = varE + 21;
+ } while (vbx < 42);
+
+ palette[19] = 21;
+}
+
+void Palette::setLowRange() {
+ _mainPalette[0] = _mainPalette[1] = _mainPalette[2] = 0;
+ _mainPalette[3] = _mainPalette[4] = _mainPalette[5] = 0x15;
+ _mainPalette[6] = _mainPalette[7] = _mainPalette[8] = 0x2A;
+ _mainPalette[9] = _mainPalette[10] = _mainPalette[11] = 0x3F;
+ _vm->_palette->setPalette(_mainPalette, 0, 4);
+}
+
+} // End of namespace MADS