/* 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 "bladerunner/zbuffer.h" #include "bladerunner/decompress_lzo.h" #include "common/debug.h" namespace BladeRunner { void ZBufferDirtyRects::reset() { _count = 0; } bool ZBufferDirtyRects::add(Common::Rect rect) { if (_count == MAX_DIRTY_RECTS) return false; _rects[_count++] = rect; if (_count > 1) { extendExisting(); } return true; } void ZBufferDirtyRects::extendExisting() { if (_count < 2) return; Common::Rect last = _rects[_count - 1]; int i; for (i = 0; i != _count - 1; ++i) { if (last.intersects(_rects[i])) { _rects[i].extend(last); --_count; break; } } } int ZBufferDirtyRects::getCount() const { return _count; } bool ZBufferDirtyRects::popRect(Common::Rect *rect) { if (_count == 0) return false; *rect = _rects[--_count]; return true; } ZBuffer::ZBuffer() { _zbuf1 = nullptr; _zbuf2 = nullptr; _dirtyRects = new ZBufferDirtyRects(); _width = 0; _height = 0; enable(); } ZBuffer::~ZBuffer() { delete[] _zbuf2; delete[] _zbuf1; delete _dirtyRects; } void ZBuffer::init(int width, int height) { _width = width; _height = height; _zbuf1 = new uint16[width * height]; _zbuf2 = new uint16[width * height]; } static int decodePartialZBuffer(const uint8 *src, uint16 *curZBUF, uint32 srcLen) { uint32 dstSize = 640 * 480; // This is taken from global variables? uint32 dstRemain = dstSize; uint16 *curzp = curZBUF; const uint16 *inp = (const uint16 *)src; while (dstRemain && (inp - (const uint16 *)src) < (ptrdiff_t)srcLen) { uint32 count = FROM_LE_16(*inp++); if (count & 0x8000) { count = MIN(count & 0x7fff, dstRemain); dstRemain -= count; while (count--) { uint16 value = FROM_LE_16(*inp++); if (value) *curzp = value; ++curzp; } } else { count = MIN(count, dstRemain); dstRemain -= count; uint16 value = FROM_LE_16(*inp++); if (!value) { curzp += count; } else { while (count--) *curzp++ = value; } } } return dstSize - dstRemain; } bool ZBuffer::decodeData(const uint8 *data, int size) { if (_disabled) { return false; } uint32 width, height, complete;// , unk0; width = READ_LE_UINT32(data + 0); height = READ_LE_UINT32(data + 4); complete = READ_LE_UINT32(data + 8); /*unk0 =*/ READ_LE_UINT32(data + 12); if (width != (uint32)_width || height != (uint32)_height) { warning("zbuffer size mismatch (%d, %d) != (%d, %d)", _width, _height, width, height); return false; } data += 16; size -= 16; if (complete) { resetUpdates(); size_t zbufOutSize; decompress_lzo1x(data, size, (uint8 *)_zbuf1, &zbufOutSize); #ifdef SCUMM_BIG_ENDIAN // As the compression is working with 8-bit data, on big-endian architectures we have to switch order of bytes in uncompressed data uint8 *rawZbuf = (uint8 *)_zbuf1; for (size_t i = 0; i < zbufOutSize - 1; i += 2) { SWAP(rawZbuf[i], rawZbuf[i + 1]); } #endif memcpy(_zbuf2, _zbuf1, 2 * _width * _height); } else { clean(); decodePartialZBuffer(data, _zbuf1, size); decodePartialZBuffer(data, _zbuf2, size); } return true; } uint16 *ZBuffer::getData() const { return _zbuf2; } uint16 ZBuffer::getZValue(int x, int y) const { assert(x >= 0 && x < _width); assert(y >= 0 && y < _height); if (_zbuf2 == nullptr) { return 0; } return _zbuf2[y * _width + x]; } void ZBuffer::blit(Common::Rect rect) { int line_width = rect.width(); for (int y = rect.top; y != rect.bottom; ++y) { int offset = y * _width + rect.left; memcpy(_zbuf2 + offset, _zbuf1 + offset, 2 * line_width); } } void ZBuffer::mark(Common::Rect rect) { assert(rect.isValidRect()); // debug("mark %d, %d, %d, %d", rect.top, rect.right, rect.bottom, rect.left); rect.clip(_width, _height); _dirtyRects->add(rect); } void ZBuffer::clean() { Common::Rect rect; while (_dirtyRects->popRect(&rect)) { // debug("blit %d, %d, %d, %d", rect.top, rect.right, rect.bottom, rect.left); blit(rect); } } void ZBuffer::resetUpdates() { _dirtyRects->reset(); } void ZBuffer::disable() { _disabled = true; } void ZBuffer::enable() { _disabled = false; } } // End of namespace BladeRunner