mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-22 18:02:05 +00:00
a0df86955f
svn-id: r54358
317 lines
7.5 KiB
C++
317 lines
7.5 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 "graphics/dither.h"
|
|
|
|
#include "common/endian.h"
|
|
#include "common/stream.h"
|
|
|
|
namespace Graphics {
|
|
|
|
PaletteLUT::PaletteLUT(byte depth, PaletteFormat format) {
|
|
assert((depth > 1) && (depth < 9));
|
|
|
|
// For adjusting depth
|
|
_depth1 = depth;
|
|
_depth2 = 2 * _depth1;
|
|
_shift = 8 - _depth1;
|
|
|
|
// The table's dimensions
|
|
_dim1 = (1 << _depth1);
|
|
_dim2 = _dim1 * _dim1;
|
|
_dim3 = _dim1 * _dim1 * _dim1;
|
|
|
|
_format = format;
|
|
|
|
// What's already built
|
|
_got = _dim1;
|
|
_gots = new byte[_dim1];
|
|
|
|
// The lookup table
|
|
_lut = new byte[_dim3];
|
|
|
|
memset(_lutPal, 0, 768);
|
|
memset(_realPal, 0, 768);
|
|
memset(_gots, 1, _dim1);
|
|
}
|
|
|
|
void PaletteLUT::setPalette(const byte *palette, PaletteFormat format,
|
|
byte depth, int transp) {
|
|
|
|
assert((depth > 1) && (depth < 9));
|
|
|
|
_transp = transp;
|
|
|
|
int shift = 8 - depth;
|
|
|
|
// Checking for the table's and the palette's pixel format
|
|
if ((_format == kPaletteRGB) && (format == kPaletteYUV)) {
|
|
byte *newPal = _realPal;
|
|
const byte *oldPal = palette;
|
|
for (int i = 0; i < 256; i++, newPal += 3, oldPal += 3)
|
|
YUV2RGB(oldPal[0] << shift, oldPal[1] << shift, oldPal[2] << shift,
|
|
newPal[0], newPal[1], newPal[2]);
|
|
} else if ((_format == kPaletteYUV) && (format == kPaletteRGB)) {
|
|
byte *newPal = _realPal;
|
|
const byte *oldPal = palette;
|
|
for (int i = 0; i < 256; i++, newPal += 3, oldPal += 3)
|
|
RGB2YUV(oldPal[0] << shift, oldPal[1] << shift, oldPal[2] << shift,
|
|
newPal[0], newPal[1], newPal[2]);
|
|
} else
|
|
memcpy(_realPal, palette, 768);
|
|
|
|
// Using the specified depth for the lookup
|
|
byte *newPal = _lutPal, *oldPal = _realPal;
|
|
for (int i = 0; i < 768; i++)
|
|
*newPal++ = (*oldPal++) >> _shift;
|
|
|
|
// Everything has to be rebuilt
|
|
_got = 0;
|
|
memset(_gots, 0, _dim1);
|
|
}
|
|
|
|
PaletteLUT::~PaletteLUT() {
|
|
delete[] _lut;
|
|
delete[] _gots;
|
|
}
|
|
|
|
void PaletteLUT::buildNext() {
|
|
if (_got >= _dim1)
|
|
return;
|
|
|
|
build(_got++);
|
|
}
|
|
|
|
#define SQR(x) ((x) * (x))
|
|
// Building one "slice"
|
|
void PaletteLUT::build(int d1) {
|
|
// First dimension
|
|
byte *lut = _lut + d1 * _dim2;
|
|
|
|
// Second dimension
|
|
for (uint32 j = 0; j < _dim1; j++) {
|
|
// Third dimension
|
|
for (uint32 k = 0; k < _dim1; k++) {
|
|
const byte *p = _lutPal;
|
|
uint32 d = 0xFFFFFFFF;
|
|
byte n = 0;
|
|
|
|
// Going over every palette entry, searching for the closest
|
|
for (int c = 0; c < 256; c++, p += 3) {
|
|
// Ignore the transparent color
|
|
if (c == _transp)
|
|
continue;
|
|
|
|
uint32 di = SQR(d1 - p[0]) + SQR(j - p[1]) + SQR(k - p[2]);
|
|
if (di < d) {
|
|
d = di;
|
|
n = c;
|
|
if (d == 0)
|
|
break;
|
|
}
|
|
}
|
|
|
|
*lut++ = n;
|
|
}
|
|
}
|
|
|
|
// Got this slice now
|
|
_gots[d1] = 1;
|
|
}
|
|
|
|
inline int PaletteLUT::getIndex(byte c1, byte c2, byte c3) const {
|
|
return ((c1 >> _shift) << _depth2) | ((c2 >> _shift) << _depth1) | (c3 >> _shift);
|
|
}
|
|
|
|
void PaletteLUT::getEntry(byte index, byte &c1, byte &c2, byte &c3) const {
|
|
c1 = _realPal[index * 3 + 0];
|
|
c2 = _realPal[index * 3 + 1];
|
|
c3 = _realPal[index * 3 + 2];
|
|
}
|
|
|
|
byte PaletteLUT::findNearest(byte c1, byte c2, byte c3) {
|
|
return _lut[getIndex(c1, c2, c3)];
|
|
}
|
|
|
|
byte PaletteLUT::findNearest(byte c1, byte c2, byte c3, byte &nC1, byte &nC2, byte &nC3) {
|
|
// If we don't have the required "slice" yet, build it
|
|
if (!_gots[c1 >> _shift])
|
|
build(c1 >> _shift);
|
|
|
|
int palIndex = _lut[getIndex(c1, c2, c3)];
|
|
int i = palIndex * 3;
|
|
|
|
nC1 = _realPal[i + 0];
|
|
nC2 = _realPal[i + 1];
|
|
nC3 = _realPal[i + 2];
|
|
|
|
return palIndex;
|
|
}
|
|
|
|
bool PaletteLUT::save(Common::WriteStream &stream) {
|
|
// The table has to be completely built before we can save
|
|
while (_got < _dim1)
|
|
buildNext();
|
|
|
|
stream.writeUint32BE(MKID_BE('PLUT')); // Magic
|
|
stream.writeUint32BE(kVersion);
|
|
stream.writeByte(_depth1);
|
|
if (stream.write(_realPal, 768) != 768)
|
|
return false;
|
|
if (stream.write(_lutPal, 768) != 768)
|
|
return false;
|
|
if (stream.write(_lut, _dim3) != _dim3)
|
|
return false;
|
|
if (!stream.flush())
|
|
return false;
|
|
|
|
if (stream.err())
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool PaletteLUT::load(Common::SeekableReadStream &stream) {
|
|
// _realPal + _lutPal + _lut + _depth1 + magic + version
|
|
int32 needSize = 768 + 768 + _dim3 + 1 + 4 + 4;
|
|
|
|
if ((stream.size() - stream.pos()) < needSize)
|
|
return false;
|
|
|
|
// Magic
|
|
if (stream.readUint32BE() != MKID_BE('PLUT'))
|
|
return false;
|
|
|
|
if (stream.readUint32BE() != kVersion)
|
|
return false;
|
|
|
|
byte depth1 = stream.readByte();
|
|
|
|
if (depth1 != _depth1)
|
|
return false;
|
|
|
|
if (stream.read(_realPal, 768) != 768)
|
|
return false;
|
|
if (stream.read(_lutPal, 768) != 768)
|
|
return false;
|
|
if (stream.read(_lut, _dim3) != _dim3)
|
|
return false;
|
|
|
|
_got = _dim1;
|
|
memset(_gots, 1, _dim1);
|
|
|
|
return true;
|
|
}
|
|
|
|
SierraLight::SierraLight(int16 width, PaletteLUT *palLUT) {
|
|
assert(width > 0);
|
|
|
|
_width = width;
|
|
_palLUT = palLUT;
|
|
|
|
// Big buffer for the errors of the current and next line
|
|
_errorBuf = new int32[3 * (2 * (_width + 2*1))];
|
|
memset(_errorBuf, 0, (3 * (2 * (_width + 2*1))) * sizeof(int32));
|
|
|
|
_curLine = 0;
|
|
_errors[0] = _errorBuf + 3;
|
|
_errors[1] = _errors[0] + 3 * (_width + 2*1);
|
|
}
|
|
|
|
SierraLight::~SierraLight() {
|
|
delete[] _errorBuf;
|
|
}
|
|
|
|
void SierraLight::newFrame() {
|
|
_curLine = 0;
|
|
memset(_errors[0], 0, 3 * _width * sizeof(int32));
|
|
memset(_errors[1], 0, 3 * _width * sizeof(int32));
|
|
}
|
|
|
|
void SierraLight::nextLine() {
|
|
// Clear the finished line, it will become the last line in the buffer
|
|
memset(_errors[_curLine], 0, 3 * _width * sizeof(int32));
|
|
|
|
_curLine = (_curLine + 1) % 2;
|
|
}
|
|
|
|
byte SierraLight::dither(byte c1, byte c2, byte c3, uint32 x) {
|
|
assert(_palLUT);
|
|
assert(x < (uint32)_width);
|
|
|
|
int32 eC1, eC2, eC3;
|
|
|
|
getErrors(x, eC1, eC2, eC3);
|
|
|
|
// Apply error on values
|
|
c1 = CLIP<int>(c1 + eC1, 0, 255);
|
|
c2 = CLIP<int>(c2 + eC2, 0, 255);
|
|
c3 = CLIP<int>(c3 + eC3, 0, 255);
|
|
|
|
// Find color
|
|
byte newC1, newC2, newC3;
|
|
byte newPixel = _palLUT->findNearest(c1, c2, c3, newC1, newC2, newC3);
|
|
|
|
// Calculate new error
|
|
eC1 = c1 - newC1;
|
|
eC2 = c2 - newC2;
|
|
eC3 = c3 - newC3;
|
|
|
|
// Add them
|
|
addErrors(x, eC1, eC2, eC3);
|
|
|
|
return newPixel;
|
|
}
|
|
|
|
inline void SierraLight::getErrors(uint32 x, int32 &eC1, int32 &eC2, int32 &eC3) {
|
|
int32 *errCur = _errors[_curLine];
|
|
|
|
x *= 3;
|
|
eC1 = errCur[x + 0] >> 2;
|
|
eC2 = errCur[x + 1] >> 2;
|
|
eC3 = errCur[x + 2] >> 2;
|
|
}
|
|
|
|
inline void SierraLight::addErrors(uint32 x, int32 eC1, int32 eC2, int32 eC3) {
|
|
int32 *errCur = _errors[_curLine];
|
|
int32 *errNext = _errors[(_curLine + 1) % 2];
|
|
|
|
// Indices for current error
|
|
int x0 = 3 * (x + 1);
|
|
int x1 = 3 * (x + 0);
|
|
int x2 = 3 * (x - 1);
|
|
|
|
errCur [x0 + 0] += eC1 << 1;
|
|
errCur [x0 + 1] += eC2 << 1;
|
|
errCur [x0 + 2] += eC3 << 1;
|
|
errNext[x1 + 0] += eC1;
|
|
errNext[x1 + 1] += eC2;
|
|
errNext[x1 + 2] += eC3;
|
|
errNext[x2 + 0] += eC1;
|
|
errNext[x2 + 1] += eC2;
|
|
errNext[x2 + 2] += eC3;
|
|
}
|
|
|
|
} // End of namespace Graphics
|