scummvm/engines/sci/gfx/palette.cpp
Willem Jan Palenstijn a19c23ab7a SCI: add (unused) palette revisioning support
svn-id: r40838
2009-05-23 22:21:42 +00:00

308 lines
7.0 KiB
C++

/* 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 "sci/gfx/palette.h"
#include "sci/gfx/gfx_system.h"
// #define DEBUG_MERGE
namespace Sci {
Palette::Palette(uint s) {
_size = s;
_colors = new PaletteEntry[s];
_parent = 0;
_dirty = true;
_refcount = 1;
_revision = 0;
}
Palette::Palette(const gfx_pixmap_color_t *colors, uint s) {
_size = s;
_colors = new PaletteEntry[s];
_parent = 0;
_dirty = true;
_refcount = 1;
_revision = 0;
for (uint i = 0; i < _size; ++i) {
_colors[i].r = colors[i].r;
_colors[i].g = colors[i].g;
_colors[i].b = colors[i].b;
_colors[i].refcount = 0;
_colors[i].parent_index = -1;
}
}
Palette::~Palette() {
if (_parent)
unmerge();
delete[] _colors;
_colors = 0;
}
Palette *Palette::getref() {
_refcount++;
return this;
}
void Palette::free() {
assert(_refcount > 0);
_refcount--;
if (_refcount == 0)
delete this;
}
void Palette::resize(uint s) {
if (s == _size)
return;
assert(!_parent);
assert(_refcount == 1);
assert(s >= _size);
PaletteEntry *n = new PaletteEntry[s];
for (uint i = 0; i < _size; ++i)
n[i] = _colors[i];
delete[] _colors;
_colors = n;
_size = s;
}
void Palette::unmerge() {
assert(_parent);
#ifdef DEBUG_MERGE
fprintf(stderr, "Unmerge %s from %s (%d colors)\n", name.c_str(), _parent->name.c_str(), _size);
#endif
if (_parent->_revision != _revision) {
#ifdef DEBUG_MERGE
fprintf(stderr, "NOP (revision mismatch)\n");
#endif
return;
}
int count = 0;
for (uint i = 0; i < _size; ++i) {
if (_colors[i].refcount == PALENTRY_FREE)
continue;
int pi = _colors[i].parent_index;
assert(pi >= 0);
assert(pi < (int)_parent->_size);
assert(_parent->_colors[pi].refcount != 0);
assert(_parent->_colors[pi].refcount != PALENTRY_FREE);
if (_parent->_colors[pi].refcount != PALENTRY_LOCKED)
_parent->_colors[pi].refcount--;
if (_parent->_colors[pi].refcount == 0) {
_parent->_colors[pi].refcount = PALENTRY_FREE;
count++;
}
_colors[i].parent_index = -1;
}
#ifdef DEBUG_MERGE
fprintf(stderr, "Unmerge free %d colors\n", count);
#endif
_parent = 0;
}
void Palette::setColor(uint index, byte r, byte g, byte b) {
assert(index < _size);
assert(!_parent);
// FIXME: We may want to have this assert. This will require changing the
// way loops sharing a single view's palette works.
// assert(_refcount == 1);
PaletteEntry& entry = _colors[index];
assert(entry.refcount == PALENTRY_FREE || entry.refcount == 0);
entry.refcount = 0;
entry.r = r;
entry.g = g;
entry.b = b;
entry.parent_index = -1;
_dirty = true;
}
void Palette::makeSystemColor(uint index, const PaletteEntry &color) {
assert(index < _size);
PaletteEntry& entry = _colors[index];
entry.r = color.r;
entry.g = color.g;
entry.b = color.b;
entry.refcount = PALENTRY_LOCKED;
}
uint Palette::findNearbyColor(byte r, byte g, byte b, bool lock) {
int bestdelta = 1 + ((0x100 * 0x100) * 3);
int bestcolor = -1;
int firstfree = -1;
assert(_size != 0);
for (uint i = 0; i < _size; ++i) {
PaletteEntry& entry = _colors[i];
if (entry.refcount != PALENTRY_FREE) {
int dr = abs(entry.r - r);
int dg = abs(entry.g - g);
int db = abs(entry.b - b);
if (dr == 0 && dg == 0 && db == 0) {
// Exact match
//exact = true;
if (lock && entry.refcount != PALENTRY_LOCKED)
entry.refcount++;
return i;
}
int delta = (dr * dr) + (dg * dg) + (db * db);
if (delta < bestdelta) {
bestdelta = delta;
bestcolor = i;
}
} else {
if (firstfree == -1)
firstfree = i;
}
}
if (firstfree != -1) {
// TODO: mark palette as dirty
setColor(firstfree, r, g, b);
//exact = true;
if (lock)
_colors[firstfree].refcount++;
return firstfree;
}
//exact = false;
if (lock && _colors[bestcolor].refcount != PALENTRY_LOCKED) {
#if 0
_colors[bestcolor].r = r;
_colors[bestcolor].g = g;
_colors[bestcolor].b = b;
_dirty = true;
#endif
_colors[bestcolor].refcount++;
}
return bestcolor;
}
bool Palette::mergeInto(Palette *parent) {
assert(!_parent || _parent == parent);
assert(parent != this);
#ifdef DEBUG_MERGE
fprintf(stderr, "Merge: %s into %s (%d colors)\n", name.c_str(), parent->name.c_str(), _size);
#endif
if (_parent == parent && parent->_revision == _revision) {
#ifdef DEBUG_MERGE
fprintf(stderr, "NOP\n");
#endif
return false;
}
_parent = parent;
_revision = parent->_revision;
#ifdef DEBUG_MERGE
bool *used = new bool[_parent->size()];
for (uint i = 0; i < _parent->size(); ++i)
used[i] = false;
int count = 0;
uint used_min = 1000;
uint used_max = 0;
#endif
for (uint i = 0; i < _size; ++i) {
PaletteEntry& entry = _colors[i];
if (entry.refcount == PALENTRY_FREE)
continue;
uint pi = _parent->findNearbyColor(entry.r, entry.g, entry.b);
#ifdef DEBUG_MERGE
if (!used[pi]) count++;
used[pi] = true;
if (pi > used_max) used_max = pi;
if (pi < used_min) used_min = pi;
#endif
entry.parent_index = pi;
if (_parent->_colors[pi].refcount != PALENTRY_LOCKED)
_parent->_colors[pi].refcount++;
}
#ifdef DEBUG_MERGE
fprintf(stderr, "Merge used %d colours in [%d..%d]\n", count, used_min, used_max);
delete[] used;
#endif
return true;
}
void Palette::forceInto(Palette *parent) {
assert(!_parent || _parent == parent);
assert(parent != this);
#ifdef DEBUG_MERGE
fprintf(stderr, "Merge: force %s into %s (%d colors)\n", name.c_str(), parent->name.c_str(), _size);
#endif
_parent = parent;
parent->_revision++;
_revision = parent->_revision;
for (unsigned int i = 0; i < _size; ++i) {
// FIXME: PALENTRY_LOCKED doesn't work well with forceInto...
if (_colors[i].refcount != PALENTRY_FREE) {
_parent->_colors[i] = _colors[i];
_parent->_colors[i].parent_index = -1;
_colors[i].parent_index = i;
if (_parent->_colors[i].refcount != PALENTRY_LOCKED)
_parent->_colors[i].refcount = 1;
} else {
_parent->_colors[i].refcount = 0;
}
}
_parent->_dirty = true;
}
Palette *Palette::copy() {
assert(!_parent);
Palette* p = new Palette(_size);
p->name = "copy of " + name;
for (uint i = 0; i < _size; ++i) {
p->_colors[i] = _colors[i];
if (p->_colors[i].refcount >= 0)
p->_colors[i].refcount = 0;
}
return p;
}
} // End of namespace Sci