1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "bladerunner/zbuffer.h"
24
25 #include "bladerunner/decompress_lzo.h"
26
27 #include "common/debug.h"
28
29 namespace BladeRunner {
30
reset()31 void ZBufferDirtyRects::reset() {
32 _count = 0;
33 }
34
add(Common::Rect rect)35 bool ZBufferDirtyRects::add(Common::Rect rect) {
36 if (_count == MAX_DIRTY_RECTS)
37 return false;
38
39 _rects[_count++] = rect;
40 if (_count > 1) {
41 extendExisting();
42 }
43 return true;
44 }
45
extendExisting()46 void ZBufferDirtyRects::extendExisting() {
47 if (_count < 2)
48 return;
49
50 Common::Rect last = _rects[_count - 1];
51
52 int i;
53 for (i = 0; i != _count - 1; ++i) {
54 if (last.intersects(_rects[i])) {
55 _rects[i].extend(last);
56 --_count;
57 break;
58 }
59 }
60 }
61
getCount() const62 int ZBufferDirtyRects::getCount() const {
63 return _count;
64 }
65
popRect(Common::Rect * rect)66 bool ZBufferDirtyRects::popRect(Common::Rect *rect) {
67 if (_count == 0)
68 return false;
69
70 *rect = _rects[--_count];
71 return true;
72 }
73
ZBuffer()74 ZBuffer::ZBuffer() {
75 _zbuf1 = nullptr;
76 _zbuf2 = nullptr;
77 _dirtyRects = new ZBufferDirtyRects();
78 _width = 0;
79 _height = 0;
80 enable();
81 }
82
~ZBuffer()83 ZBuffer::~ZBuffer() {
84 delete[] _zbuf2;
85 delete[] _zbuf1;
86 delete _dirtyRects;
87 }
88
init(int width,int height)89 void ZBuffer::init(int width, int height) {
90 _width = width;
91 _height = height;
92
93 _zbuf1 = new uint16[width * height];
94 _zbuf2 = new uint16[width * height];
95 }
96
decodePartialZBuffer(const uint8 * src,uint16 * curZBUF,uint32 srcLen)97 static int decodePartialZBuffer(const uint8 *src, uint16 *curZBUF, uint32 srcLen) {
98 uint32 dstSize = 640 * 480; // This is taken from global variables?
99 uint32 dstRemain = dstSize;
100
101 uint16 *curzp = curZBUF;
102 const uint16 *inp = (const uint16 *)src;
103
104 while (dstRemain && (inp - (const uint16 *)src) < (ptrdiff_t)srcLen) {
105 uint32 count = FROM_LE_16(*inp++);
106
107 if (count & 0x8000) {
108 count = MIN(count & 0x7fff, dstRemain);
109 dstRemain -= count;
110
111 while (count--) {
112 uint16 value = FROM_LE_16(*inp++);
113 if (value)
114 *curzp = value;
115 ++curzp;
116 }
117 } else {
118 count = MIN(count, dstRemain);
119 dstRemain -= count;
120 uint16 value = FROM_LE_16(*inp++);
121
122 if (!value) {
123 curzp += count;
124 } else {
125 while (count--)
126 *curzp++ = value;
127 }
128 }
129 }
130 return dstSize - dstRemain;
131 }
132
decodeData(const uint8 * data,int size)133 bool ZBuffer::decodeData(const uint8 *data, int size) {
134 if (_disabled) {
135 return false;
136 }
137
138 uint32 width, height, complete;// , unk0;
139
140 width = READ_LE_UINT32(data + 0);
141 height = READ_LE_UINT32(data + 4);
142 complete = READ_LE_UINT32(data + 8);
143 /*unk0 =*/ READ_LE_UINT32(data + 12);
144
145 if (width != (uint32)_width || height != (uint32)_height) {
146 warning("zbuffer size mismatch (%d, %d) != (%d, %d)", _width, _height, width, height);
147 return false;
148 }
149
150 data += 16;
151 size -= 16;
152
153 if (complete) {
154 resetUpdates();
155 size_t zbufOutSize;
156 decompress_lzo1x(data, size, (uint8 *)_zbuf1, &zbufOutSize);
157 #ifdef SCUMM_BIG_ENDIAN
158 // As the compression is working with 8-bit data, on big-endian architectures we have to switch order of bytes in uncompressed data
159 uint8 *rawZbuf = (uint8 *)_zbuf1;
160 for (size_t i = 0; i < zbufOutSize - 1; i += 2) {
161 SWAP(rawZbuf[i], rawZbuf[i + 1]);
162 }
163 #endif
164 memcpy(_zbuf2, _zbuf1, 2 * _width * _height);
165 } else {
166 clean();
167 decodePartialZBuffer(data, _zbuf1, size);
168 decodePartialZBuffer(data, _zbuf2, size);
169 }
170
171 return true;
172 }
173
getData() const174 uint16 *ZBuffer::getData() const {
175 return _zbuf2;
176 }
177
getZValue(int x,int y) const178 uint16 ZBuffer::getZValue(int x, int y) const {
179 assert(x >= 0 && x < _width);
180 assert(y >= 0 && y < _height);
181
182 if (_zbuf2 == nullptr) {
183 return 0;
184 }
185
186 return _zbuf2[y * _width + x];
187 }
188
blit(Common::Rect rect)189 void ZBuffer::blit(Common::Rect rect) {
190 int line_width = rect.width();
191
192 for (int y = rect.top; y != rect.bottom; ++y) {
193 int offset = y * _width + rect.left;
194 memcpy(_zbuf2 + offset, _zbuf1 + offset, 2 * line_width);
195 }
196 }
197
mark(Common::Rect rect)198 void ZBuffer::mark(Common::Rect rect) {
199 assert(rect.isValidRect());
200
201 // debug("mark %d, %d, %d, %d", rect.top, rect.right, rect.bottom, rect.left);
202 rect.clip(_width, _height);
203 _dirtyRects->add(rect);
204 }
205
clean()206 void ZBuffer::clean() {
207 Common::Rect rect;
208 while (_dirtyRects->popRect(&rect)) {
209 // debug("blit %d, %d, %d, %d", rect.top, rect.right, rect.bottom, rect.left);
210 blit(rect);
211 }
212 }
213
resetUpdates()214 void ZBuffer::resetUpdates() {
215 _dirtyRects->reset();
216 }
217
disable()218 void ZBuffer::disable() {
219 _disabled = true;
220 }
221
enable()222 void ZBuffer::enable() {
223 _disabled = false;
224 }
225
226 } // End of namespace BladeRunner
227