diff options
Diffstat (limited to 'graphics/dither.cpp')
-rw-r--r-- | graphics/dither.cpp | 296 |
1 files changed, 296 insertions, 0 deletions
diff --git a/graphics/dither.cpp b/graphics/dither.cpp new file mode 100644 index 0000000000..21f5ded8c3 --- /dev/null +++ b/graphics/dither.cpp @@ -0,0 +1,296 @@ +/* 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" + +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) { + assert((depth > 1) && (depth < 9)); + + 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) { + 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.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 + int32 needSize = 768 + 768 + _dim3 + 1; + + if ((stream.size() - stream.pos()) < needSize) + 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, int16 height, PaletteLUT *palLUT) { + assert((width > 0) && (height > 0)); + + _width = width; + _height = height; + _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); + + 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 |