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 * Additional copyright for this file:
8 * Copyright (C) 1994-1998 Revolution Software Ltd.
9 *
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License
12 * as published by the Free Software Foundation; either version 2
13 * of the License, or (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 */
24
25
26 #include "common/endian.h"
27 #include "common/system.h"
28
29 #include "graphics/primitives.h"
30
31 #include "sword2/sword2.h"
32 #include "sword2/defs.h"
33 #include "sword2/screen.h"
34
35 namespace Sword2 {
36
37 #define RENDERAVERAGETOTAL 4
38
updateRect(Common::Rect * r)39 void Screen::updateRect(Common::Rect *r) {
40 _vm->_system->copyRectToScreen(_buffer + r->top * _screenWide + r->left,
41 _screenWide, r->left, r->top, r->right - r->left,
42 r->bottom - r->top);
43 }
44
blitBlockSurface(BlockSurface * s,Common::Rect * r,Common::Rect * clipRect)45 void Screen::blitBlockSurface(BlockSurface *s, Common::Rect *r, Common::Rect *clipRect) {
46 if (!r->intersects(*clipRect))
47 return;
48
49 byte *src = s->data;
50
51 if (r->top < clipRect->top) {
52 src -= BLOCKWIDTH * (r->top - clipRect->top);
53 r->top = clipRect->top;
54 }
55 if (r->left < clipRect->left) {
56 src -= (r->left - clipRect->left);
57 r->left = clipRect->left;
58 }
59 if (r->bottom > clipRect->bottom)
60 r->bottom = clipRect->bottom;
61 if (r->right > clipRect->right)
62 r->right = clipRect->right;
63
64 byte *dst = _buffer + r->top * _screenWide + r->left;
65 int i;
66
67 if (s->transparent) {
68 for (i = 0; i < r->bottom - r->top; i++) {
69 for (int j = 0; j < r->right - r->left; j++) {
70 if (src[j])
71 dst[j] = src[j];
72 }
73 src += BLOCKWIDTH;
74 dst += _screenWide;
75 }
76 } else {
77 for (i = 0; i < r->bottom - r->top; i++) {
78 memcpy(dst, src, r->right - r->left);
79 src += BLOCKWIDTH;
80 dst += _screenWide;
81 }
82 }
83 }
84
85 // There are two different separate functions for scaling the image - one fast
86 // and one good. Or at least that's the theory. I'm sure there are better ways
87 // to scale an image than this. The latter is used at the highest graphics
88 // quality setting. Note that the "good" scaler takes extra parameters so that
89 // it can use the background image when calculating the average pixel value.
90 //
91 // This code isn't quite like the original DrawSprite(), but the result should
92 // be close enough, I hope.
93
scaleImageFast(byte * dst,uint16 dstPitch,uint16 dstWidth,uint16 dstHeight,byte * src,uint16 srcPitch,uint16 srcWidth,uint16 srcHeight)94 void Screen::scaleImageFast(byte *dst, uint16 dstPitch, uint16 dstWidth, uint16 dstHeight, byte *src, uint16 srcPitch, uint16 srcWidth, uint16 srcHeight) {
95 int x, y;
96
97 for (x = 0; x < dstWidth; x++)
98 _xScale[x] = (x * srcWidth) / dstWidth;
99
100 for (y = 0; y < dstHeight; y++)
101 _yScale[y] = (y * srcHeight) / dstHeight;
102
103 for (y = 0; y < dstHeight; y++) {
104 for (x = 0; x < dstWidth; x++) {
105 dst[x] = src[_yScale[y] * srcPitch + _xScale[x]];
106 }
107 dst += dstPitch;
108 }
109 }
110
scaleImageGood(byte * dst,uint16 dstPitch,uint16 dstWidth,uint16 dstHeight,byte * src,uint16 srcPitch,uint16 srcWidth,uint16 srcHeight,byte * backBuf,int16 bbXPos,int16 bbYPos)111 void Screen::scaleImageGood(byte *dst, uint16 dstPitch, uint16 dstWidth, uint16 dstHeight, byte *src, uint16 srcPitch, uint16 srcWidth, uint16 srcHeight, byte *backBuf, int16 bbXPos, int16 bbYPos) {
112 for (int y = 0; y < dstHeight; y++) {
113 for (int x = 0; x < dstWidth; x++) {
114 uint8 c1, c2, c3, c4;
115
116 uint32 xPos = (x * srcWidth) / dstWidth;
117 uint32 yPos = (y * srcHeight) / dstHeight;
118 uint32 xFrac = dstWidth - (x * srcWidth) % dstWidth;
119 uint32 yFrac = dstHeight - (y * srcHeight) % dstHeight;
120
121 byte *srcPtr = src + yPos * srcPitch + xPos;
122
123 bool transparent = true;
124
125 if (*srcPtr) {
126 c1 = *srcPtr;
127 transparent = false;
128 } else {
129 if (bbXPos + x >= 0 &&
130 bbXPos + x < RENDERWIDE &&
131 bbYPos + y >= MENUDEEP &&
132 bbYPos + y < MENUDEEP + RENDERDEEP) {
133 c1 = *(backBuf + _screenWide * (bbYPos + y) + bbXPos + x);
134 } else {
135 c1 = 0;
136 }
137 }
138
139 if (x < dstWidth - 1) {
140 if (*(srcPtr + 1)) {
141 c2 = *(srcPtr + 1);
142 transparent = false;
143 } else {
144 if (bbXPos + x + 1 >= 0 &&
145 bbXPos + x + 1 < RENDERWIDE &&
146 bbYPos + y >= MENUDEEP &&
147 bbYPos + y + 1 < MENUDEEP + RENDERDEEP) {
148 c2 = *(backBuf + _screenWide * (bbYPos + y) + bbXPos + x + 1);
149 } else {
150 c2 = c1;
151 }
152 }
153 } else {
154 c2 = c1;
155 }
156
157 if (y < dstHeight - 1) {
158 if (*(srcPtr + srcPitch)) {
159 c3 = *(srcPtr + srcPitch);
160 transparent = false;
161 } else {
162 if (bbXPos + x >= 0 &&
163 bbXPos + x < RENDERWIDE &&
164 bbYPos + y + 1 >= MENUDEEP &&
165 bbYPos + y + 1 < MENUDEEP + RENDERDEEP) {
166 c3 = *(backBuf + _screenWide * (bbYPos + y + 1) + bbXPos);
167 } else {
168 c3 = c1;
169 }
170 }
171 } else {
172 c3 = c1;
173 }
174
175 if (x < dstWidth - 1 && y < dstHeight - 1) {
176 if (*(srcPtr + srcPitch + 1)) {
177 c4 = *(srcPtr + srcPitch + 1);
178 transparent = false;
179 } else {
180 if (bbXPos + x + 1 >= 0 &&
181 bbXPos + x + 1 < RENDERWIDE &&
182 bbYPos + y + 1 >= MENUDEEP &&
183 bbYPos + y + 1 < MENUDEEP + RENDERDEEP) {
184 c4 = *(backBuf + _screenWide * (bbYPos + y + 1) + bbXPos + x + 1);
185 } else {
186 c4 = c3;
187 }
188 }
189 } else {
190 c4 = c3;
191 }
192
193 if (!transparent) {
194 uint32 r1 = _palette[c1 * 3 + 0];
195 uint32 g1 = _palette[c1 * 3 + 1];
196 uint32 b1 = _palette[c1 * 3 + 2];
197
198 uint32 r2 = _palette[c2 * 3 + 0];
199 uint32 g2 = _palette[c2 * 3 + 1];
200 uint32 b2 = _palette[c2 * 3 + 2];
201
202 uint32 r3 = _palette[c3 * 3 + 0];
203 uint32 g3 = _palette[c3 * 3 + 1];
204 uint32 b3 = _palette[c3 * 3 + 2];
205
206 uint32 r4 = _palette[c4 * 3 + 0];
207 uint32 g4 = _palette[c4 * 3 + 1];
208 uint32 b4 = _palette[c4 * 3 + 2];
209
210 uint32 r5 = (r1 * xFrac + r2 * (dstWidth - xFrac)) / dstWidth;
211 uint32 g5 = (g1 * xFrac + g2 * (dstWidth - xFrac)) / dstWidth;
212 uint32 b5 = (b1 * xFrac + b2 * (dstWidth - xFrac)) / dstWidth;
213
214 uint32 r6 = (r3 * xFrac + r4 * (dstWidth - xFrac)) / dstWidth;
215 uint32 g6 = (g3 * xFrac + g4 * (dstWidth - xFrac)) / dstWidth;
216 uint32 b6 = (b3 * xFrac + b4 * (dstWidth - xFrac)) / dstWidth;
217
218 uint32 r = (r5 * yFrac + r6 * (dstHeight - yFrac)) / dstHeight;
219 uint32 g = (g5 * yFrac + g6 * (dstHeight - yFrac)) / dstHeight;
220 uint32 b = (b5 * yFrac + b6 * (dstHeight - yFrac)) / dstHeight;
221
222 dst[y * dstWidth + x] = quickMatch(r, g, b);
223 } else
224 dst[y * dstWidth + x] = 0;
225 }
226 }
227 }
228
229 /**
230 * Plots a point relative to the top left corner of the screen. This is only
231 * used for debugging.
232 * @param x x-coordinate of the point
233 * @param y y-coordinate of the point
234 * @param color color of the point
235 */
236
plotPoint(int x,int y,uint8 color)237 void Screen::plotPoint(int x, int y, uint8 color) {
238 byte *buf = _buffer + MENUDEEP * RENDERWIDE;
239
240 x -= _scrollX;
241 y -= _scrollY;
242
243 if (x >= 0 && x < RENDERWIDE && y >= 0 && y < RENDERDEEP) {
244 buf[y * RENDERWIDE + x] = color;
245 markAsDirty(x, y + MENUDEEP, x, y + MENUDEEP);
246 }
247 }
248
plot(int x,int y,int color,void * data)249 static void plot(int x, int y, int color, void *data) {
250 Screen *screen = (Screen *)data;
251 screen->plotPoint(x, y, (uint8) color);
252 }
253
254 /**
255 * Draws a line from one point to another. This is only used for debugging.
256 * @param x0 x-coordinate of the start point
257 * @param y0 y-coordinate of the start point
258 * @param x1 x-coordinate of the end point
259 * @param y1 y-coordinate of the end point
260 * @param color color of the line
261 */
262
drawLine(int x0,int y0,int x1,int y1,uint8 color)263 void Screen::drawLine(int x0, int y0, int x1, int y1, uint8 color) {
264 Graphics::drawLine(x0, y0, x1, y1, color, &plot, this);
265 }
266
267 /**
268 * This function tells the driver the size of the background screen for the
269 * current location.
270 * @param w width of the current location
271 * @param h height of the current location
272 */
273
setLocationMetrics(uint16 w,uint16 h)274 void Screen::setLocationMetrics(uint16 w, uint16 h) {
275 _locationWide = w;
276 _locationDeep = h;
277 setNeedFullRedraw();
278 }
279
280 /**
281 * Draws a parallax layer at the current position determined by the scroll. A
282 * parallax can be either foreground, background or the main screen.
283 */
284
renderParallax(byte * ptr,int16 l)285 void Screen::renderParallax(byte *ptr, int16 l) {
286 int16 x, y;
287 uint16 xRes, yRes;
288 Common::Rect r;
289
290 if (!ptr)
291 return;
292
293 // Fetch resolution data from parallax
294
295 if (Sword2Engine::isPsx()) {
296 xRes = READ_LE_UINT16(ptr);
297 yRes = READ_LE_UINT16(ptr + 2) * 2;
298 } else {
299 Parallax p;
300
301 p.read(ptr);
302 xRes = p.w;
303 yRes = p.h;
304 }
305
306 if (_locationWide == _screenWide)
307 x = 0;
308 else
309 x = ((int32)((xRes - _screenWide) * _scrollX) / (int32)(_locationWide - _screenWide));
310
311 if (_locationDeep == _screenDeep - MENUDEEP * 2)
312 y = 0;
313 else
314 y = ((int32)((yRes - (_screenDeep - MENUDEEP * 2)) * _scrollY) / (int32)(_locationDeep - (_screenDeep - MENUDEEP * 2)));
315
316 Common::Rect clipRect;
317
318 // Leave enough space for the top and bottom menues
319
320 clipRect.left = 0;
321 clipRect.right = _screenWide;
322 clipRect.top = MENUDEEP;
323 clipRect.bottom = _screenDeep - MENUDEEP;
324
325 for (int j = 0; j < _yBlocks[l]; j++) {
326 for (int i = 0; i < _xBlocks[l]; i++) {
327 if (_blockSurfaces[l][i + j * _xBlocks[l]]) {
328 r.left = i * BLOCKWIDTH - x;
329 r.right = r.left + BLOCKWIDTH;
330 r.top = j * BLOCKHEIGHT - y + MENUDEEP;
331 r.bottom = r.top + BLOCKHEIGHT;
332 blitBlockSurface(_blockSurfaces[l][i + j * _xBlocks[l]], &r, &clipRect);
333 }
334 }
335 }
336
337 _parallaxScrollX = _scrollX - x;
338 _parallaxScrollY = _scrollY - y;
339 }
340
341 // Uncomment this when benchmarking the drawing routines.
342 #define LIMIT_FRAME_RATE
343
344 /**
345 * Initializes the timers before the render loop is entered.
346 */
347
initializeRenderCycle()348 void Screen::initializeRenderCycle() {
349 _initialTime = _vm->_system->getMillis();
350 _totalTime = _initialTime + (1000 / _vm->getFramesPerSecond());
351 }
352
353 /**
354 * This function should be called when the game engine is ready to start the
355 * render cycle.
356 */
357
startRenderCycle()358 void Screen::startRenderCycle() {
359 _scrollXOld = _scrollX;
360 _scrollYOld = _scrollY;
361
362 _startTime = _vm->_system->getMillis();
363
364 if (_startTime + _renderAverageTime >= _totalTime) {
365 _scrollX = _scrollXTarget;
366 _scrollY = _scrollYTarget;
367 _renderTooSlow = true;
368 } else {
369 _scrollX = (int16)(_scrollXOld + ((_scrollXTarget - _scrollXOld) * (_startTime - _initialTime + _renderAverageTime)) / (_totalTime - _initialTime));
370 _scrollY = (int16)(_scrollYOld + ((_scrollYTarget - _scrollYOld) * (_startTime - _initialTime + _renderAverageTime)) / (_totalTime - _initialTime));
371 _renderTooSlow = false;
372 }
373
374 if (_scrollXOld != _scrollX || _scrollYOld != _scrollY)
375 setNeedFullRedraw();
376
377 _framesPerGameCycle = 0;
378 }
379
380 /**
381 * This function should be called at the end of the render cycle.
382 * @return true if the render cycle is to be terminated,
383 * or false if it should continue
384 */
385
endRenderCycle()386 bool Screen::endRenderCycle() {
387 static int32 renderTimeLog[4] = { 60, 60, 60, 60 };
388 static int32 renderCountIndex = 0;
389 int32 time;
390
391 time = _vm->_system->getMillis();
392 renderTimeLog[renderCountIndex] = time - _startTime;
393 _startTime = time;
394 _renderAverageTime = (renderTimeLog[0] + renderTimeLog[1] + renderTimeLog[2] + renderTimeLog[3]) >> 2;
395
396 _framesPerGameCycle++;
397
398 if (++renderCountIndex == RENDERAVERAGETOTAL)
399 renderCountIndex = 0;
400
401 if (_renderTooSlow) {
402 initializeRenderCycle();
403 return true;
404 }
405
406 if (_startTime + _renderAverageTime >= _totalTime) {
407 _totalTime += (1000 / _vm->getFramesPerSecond());
408 _initialTime = time;
409 return true;
410 }
411
412 #ifdef LIMIT_FRAME_RATE
413 if (_scrollXTarget == _scrollX && _scrollYTarget == _scrollY) {
414 // If we have already reached the scroll target sleep for the
415 // rest of the render cycle.
416 _vm->sleepUntil(_totalTime);
417 _initialTime = _vm->_system->getMillis();
418 _totalTime += (1000 / _vm->getFramesPerSecond());
419 return true;
420 }
421 #endif
422
423 // This is an attempt to ensure that we always reach the scroll target.
424 // Otherwise the game frequently tries to pump out new interpolation
425 // frames without ever getting anywhere.
426
427 if (ABS(_scrollX - _scrollXTarget) <= 1 && ABS(_scrollY - _scrollYTarget) <= 1) {
428 _scrollX = _scrollXTarget;
429 _scrollY = _scrollYTarget;
430 } else {
431 _scrollX = (int16)(_scrollXOld + ((_scrollXTarget - _scrollXOld) * (_startTime - _initialTime + _renderAverageTime)) / (_totalTime - _initialTime));
432 _scrollY = (int16)(_scrollYOld + ((_scrollYTarget - _scrollYOld) * (_startTime - _initialTime + _renderAverageTime)) / (_totalTime - _initialTime));
433 }
434
435 if (_scrollX != _scrollXOld || _scrollY != _scrollYOld)
436 setNeedFullRedraw();
437
438 #ifdef LIMIT_FRAME_RATE
439 // Give the other threads some breathing space. This apparently helps
440 // against bug #875683, though I was never able to reproduce it for
441 // myself.
442 _vm->_system->delayMillis(10);
443 #endif
444
445 return false;
446 }
447
448 /**
449 * Reset scrolling stuff. This function is called from initBackground()
450 */
451
resetRenderEngine()452 void Screen::resetRenderEngine() {
453 _parallaxScrollX = 0;
454 _parallaxScrollY = 0;
455 _scrollX = 0;
456 _scrollY = 0;
457 }
458
459 /**
460 * This function should be called five times with either the parallax layer
461 * or a NULL pointer in order of background parallax to foreground parallax.
462 */
463
initializeBackgroundLayer(byte * parallax)464 int32 Screen::initializeBackgroundLayer(byte *parallax) {
465 Parallax p;
466 uint16 i, j, k;
467 byte *data;
468 byte *dst;
469
470 debug(2, "initializeBackgroundLayer");
471
472 assert(_layer < MAXLAYERS);
473
474 if (!parallax) {
475 _layer++;
476 return RD_OK;
477 }
478
479 p.read(parallax);
480
481 _xBlocks[_layer] = (p.w + BLOCKWIDTH - 1) / BLOCKWIDTH;
482 _yBlocks[_layer] = (p.h + BLOCKHEIGHT - 1) / BLOCKHEIGHT;
483
484 _blockSurfaces[_layer] = (BlockSurface **)calloc(_xBlocks[_layer] * _yBlocks[_layer], sizeof(BlockSurface *));
485 if (!_blockSurfaces[_layer])
486 return RDERR_OUTOFMEMORY;
487
488 // Decode the parallax layer into a large chunk of memory
489
490 byte *memchunk = (byte *)calloc(_xBlocks[_layer] * _yBlocks[_layer], BLOCKWIDTH * BLOCKHEIGHT);
491 if (!memchunk)
492 return RDERR_OUTOFMEMORY;
493
494 for (i = 0; i < p.h; i++) {
495 uint32 p_offset = READ_LE_UINT32(parallax + Parallax::size() + 4 * i);
496
497 if (!p_offset)
498 continue;
499
500 byte *pLine = parallax + p_offset;
501 uint16 packets = READ_LE_UINT16(pLine);
502 uint16 offset = READ_LE_UINT16(pLine + 2);
503
504 data = pLine + 4;
505 dst = memchunk + i * p.w + offset;
506
507 if (!packets) {
508 memcpy(dst, data, p.w);
509 continue;
510 }
511
512 bool zeros = false;
513
514 for (j = 0; j < packets; j++) {
515 if (zeros) {
516 dst += *data;
517 offset += *data;
518 data++;
519 zeros = false;
520 } else if (!*data) {
521 data++;
522 zeros = true;
523 } else {
524 uint16 count = *data++;
525 memcpy(dst, data, count);
526 data += count;
527 dst += count;
528 offset += count;
529 zeros = true;
530 }
531 }
532 }
533
534 // The large memory chunk is now divided into a number of smaller
535 // surfaces. For most parallax layers, we'll end up using less memory
536 // this way, and it will be faster to draw since completely transparent
537 // surfaces are discarded.
538
539 for (i = 0; i < _xBlocks[_layer] * _yBlocks[_layer]; i++) {
540 bool block_has_data = false;
541 bool block_is_transparent = false;
542
543 int x = BLOCKWIDTH * (i % _xBlocks[_layer]);
544 int y = BLOCKHEIGHT * (i / _xBlocks[_layer]);
545
546 data = memchunk + p.w * y + x;
547
548 for (j = 0; j < BLOCKHEIGHT; j++) {
549 for (k = 0; k < BLOCKWIDTH; k++) {
550 if (x + k < p.w && y + j < p.h) {
551 if (data[j * p.w + k])
552 block_has_data = true;
553 else
554 block_is_transparent = true;
555 }
556 }
557 }
558
559 // Only assign a surface to the block if it contains data.
560
561 if (block_has_data) {
562 _blockSurfaces[_layer][i] = (BlockSurface *)malloc(sizeof(BlockSurface));
563
564 // Copy the data into the surfaces.
565 dst = _blockSurfaces[_layer][i]->data;
566 for (j = 0; j < BLOCKHEIGHT; j++) {
567 memcpy(dst, data, BLOCKWIDTH);
568 data += p.w;
569 dst += BLOCKWIDTH;
570 }
571
572 _blockSurfaces[_layer][i]->transparent = block_is_transparent;
573
574 } else
575 _blockSurfaces[_layer][i] = NULL;
576 }
577
578 free(memchunk);
579 _layer++;
580
581 return RD_OK;
582 }
583
584 /**
585 * This converts PSX format background data into a format that
586 * can be understood by renderParallax functions.
587 * PSX Backgrounds are divided into tiles of 64x32 (with aspect
588 * ratio correction), while PC backgrounds are in tiles of 64x64.
589 */
590
initializePsxBackgroundLayer(byte * parallax)591 int32 Screen::initializePsxBackgroundLayer(byte *parallax) {
592 uint16 bgXres, bgYres;
593 uint16 trueXres, stripeNumber, totStripes;
594 uint32 baseAddress, stripePos;
595 uint16 i, j;
596 byte *dst;
597
598 debug(2, "initializePsxBackgroundLayer");
599
600 assert(_layer < MAXLAYERS);
601
602 if (!parallax) {
603 _layer++;
604 return RD_OK;
605 }
606
607 // Fetch data from buffer
608
609 bgXres = READ_LE_UINT16(parallax);
610 bgYres = READ_LE_UINT16(parallax + 2) * 2;
611 baseAddress = READ_LE_UINT32(parallax + 4);
612 parallax += 8;
613
614 // Calculate TRUE resolution of background, must be
615 // a multiple of 64
616
617 trueXres = (bgXres % 64) ? ((bgXres/64) + 1) * 64 : bgXres;
618 totStripes = trueXres / 64;
619
620 _xBlocks[_layer] = (bgXres + BLOCKWIDTH - 1) / BLOCKWIDTH;
621 _yBlocks[_layer] = (bgYres + BLOCKHEIGHT - 1) / BLOCKHEIGHT;
622
623 uint16 remLines = bgYres % 64;
624
625 byte *tileChunk = (byte *)malloc(BLOCKHEIGHT * BLOCKWIDTH);
626 if (!tileChunk)
627 return RDERR_OUTOFMEMORY;
628
629 _blockSurfaces[_layer] = (BlockSurface **)calloc(_xBlocks[_layer] * _yBlocks[_layer], sizeof(BlockSurface *));
630 if (!_blockSurfaces[_layer]) {
631 free(tileChunk);
632 return RDERR_OUTOFMEMORY;
633 }
634
635 // Group PSX background (64x32, when stretched vertically) tiles together,
636 // to make them compatible with pc version (composed by 64x64 tiles)
637
638 stripeNumber = 0;
639 stripePos = 0;
640 for (i = 0; i < _xBlocks[_layer] * _yBlocks[_layer]; i++) {
641 bool block_has_data = false;
642 bool block_is_transparent = false;
643
644 int posX = i / _yBlocks[_layer];
645 int posY = i % _yBlocks[_layer];
646
647 uint32 stripeOffset = READ_LE_UINT32(parallax + stripeNumber * 8 + 4) + stripePos - baseAddress;
648
649 memset(tileChunk, 1, BLOCKHEIGHT * BLOCKWIDTH);
650
651 if (!(remLines && posY == _yBlocks[_layer] - 1))
652 remLines = 32;
653
654 for (j = 0; j < remLines; j++) {
655 memcpy(tileChunk + j * BLOCKWIDTH * 2, parallax + stripeOffset + j * BLOCKWIDTH, BLOCKWIDTH);
656 memcpy(tileChunk + j * BLOCKWIDTH * 2 + BLOCKWIDTH, parallax + stripeOffset + j * BLOCKWIDTH, BLOCKWIDTH);
657 }
658
659 for (j = 0; j < BLOCKHEIGHT * BLOCKWIDTH; j++) {
660 if (tileChunk[j])
661 block_has_data = true;
662 else
663 block_is_transparent = true;
664 }
665
666 int tileIndex = totStripes * posY + posX;
667
668 // Only assign a surface to the block if it contains data.
669
670 if (block_has_data) {
671 _blockSurfaces[_layer][tileIndex] = (BlockSurface *)malloc(sizeof(BlockSurface));
672
673 // Copy the data into the surfaces.
674 dst = _blockSurfaces[_layer][tileIndex]->data;
675 memcpy(dst, tileChunk, BLOCKWIDTH * BLOCKHEIGHT);
676
677 _blockSurfaces[_layer][tileIndex]->transparent = block_is_transparent;
678
679 } else
680 _blockSurfaces[_layer][tileIndex] = NULL;
681
682 if (posY == _yBlocks[_layer] - 1) {
683 stripeNumber++;
684 stripePos = 0;
685 } else {
686 stripePos += 0x800;
687 }
688 }
689
690 free(tileChunk);
691 _layer++;
692
693 return RD_OK;
694 }
695
696 /**
697 * This converts PSX format parallax data into a format that
698 * can be understood by renderParallax functions.
699 */
700
initializePsxParallaxLayer(byte * parallax)701 int32 Screen::initializePsxParallaxLayer(byte *parallax) {
702 uint16 i, j, k;
703 byte *data;
704 byte *dst;
705
706 debug(2, "initializePsxParallaxLayer");
707
708 assert(_layer < MAXLAYERS);
709
710 if (!parallax) {
711 _layer++;
712 return RD_OK;
713 }
714
715 // uint16 plxXres = READ_LE_UINT16(parallax);
716 // uint16 plxYres = READ_LE_UINT16(parallax + 2);
717 uint16 xTiles = READ_LE_UINT16(parallax + 4);
718 uint16 yTiles = READ_LE_UINT16(parallax + 6);
719
720 // Beginning of parallax table composed by uint32,
721 // if word is 0, corresponding tile contains no data and must be skipped,
722 // if word is 0x400 tile contains data.
723 parallax += 8;
724
725 // Beginning if tiles data.
726 data = parallax + xTiles * yTiles * 4;
727
728 _xBlocks[_layer] = xTiles;
729 _yBlocks[_layer] = (yTiles / 2) + ((yTiles % 2) ? 1 : 0);
730 bool oddTiles = ((yTiles % 2) ? true : false);
731
732 _blockSurfaces[_layer] = (BlockSurface **)calloc(_xBlocks[_layer] * _yBlocks[_layer], sizeof(BlockSurface *));
733 if (!_blockSurfaces[_layer])
734 return RDERR_OUTOFMEMORY;
735
736 // We have to check two tiles for every block in PSX version, if one of those
737 // has data in it, the whole block has data. Also, tiles must be doublelined to
738 // get correct aspect ratio.
739 for (i = 0; i < _xBlocks[_layer] * _yBlocks[_layer]; i++) {
740 bool block_has_data = false;
741 bool block_is_transparent = false;
742 bool firstTilePresent, secondTilePresent;
743
744 int posX = i / _yBlocks[_layer];
745 int posY = i % _yBlocks[_layer];
746
747 if (oddTiles && posY == _yBlocks[_layer] - 1) {
748 firstTilePresent = READ_LE_UINT32(parallax) == 0x400;
749 secondTilePresent = false;
750 parallax += 4;
751 } else {
752 firstTilePresent = READ_LE_UINT32(parallax) == 0x400;
753 secondTilePresent = READ_LE_UINT32(parallax + 4) == 0x400;
754 parallax += 8;
755 }
756
757 // If one of the two grouped tiles has data, then the whole block has data
758 if (firstTilePresent || secondTilePresent) {
759 block_has_data = true;
760
761 // If one of the two grouped blocks is without data, then we also have transparency
762 if (!firstTilePresent || !secondTilePresent)
763 block_is_transparent = true;
764 }
765
766 // Now do a second check to see if we have a partially transparent block
767 if (block_has_data && !block_is_transparent) {
768 byte *block = data;
769 if (firstTilePresent) {
770 for (k = 0; k < 0x400; k++) {
771 if (*(block + k) == 0) {
772 block_is_transparent = true;
773 break;
774 }
775 }
776 block += 0x400; // On to next block...
777 }
778
779 // If we didn't find transparency in first block and we have
780 // a second tile, check it
781 if (secondTilePresent && !block_is_transparent) {
782 for (k = 0; k < 0x400; k++) {
783 if (*(block + k) == 0) {
784 block_is_transparent = true;
785 break;
786 }
787 }
788 }
789 }
790
791 int tileIndex = xTiles * posY + posX;
792
793 // Only assign a surface to the block if it contains data.
794
795 if (block_has_data) {
796 _blockSurfaces[_layer][tileIndex] = (BlockSurface *)malloc(sizeof(BlockSurface));
797 memset(_blockSurfaces[_layer][tileIndex], 0, BLOCKHEIGHT * BLOCKWIDTH);
798
799 // Copy the data into the surfaces.
800 dst = _blockSurfaces[_layer][tileIndex]->data;
801
802 if (firstTilePresent) { //There is data in the first tile
803 for (j = 0; j < 16; j++) {
804 memcpy(dst, data, BLOCKWIDTH);
805 dst += BLOCKWIDTH;
806 memcpy(dst, data, BLOCKWIDTH);
807 dst += BLOCKWIDTH;
808 data += BLOCKWIDTH;
809 }
810 } else {
811 dst += 0x800;
812 }
813
814 if (secondTilePresent) {
815 for (j = 0; j < 16; j++) {
816 memcpy(dst, data, BLOCKWIDTH);
817 dst += BLOCKWIDTH;
818 memcpy(dst, data, BLOCKWIDTH);
819 dst += BLOCKWIDTH;
820 data += BLOCKWIDTH;
821 }
822 }
823
824 _blockSurfaces[_layer][tileIndex]->transparent = block_is_transparent;
825 } else
826 _blockSurfaces[_layer][tileIndex] = NULL;
827 }
828
829 _layer++;
830
831 return RD_OK;
832 }
833
834 /**
835 * Should be called once after leaving the room to free up memory.
836 */
837
closeBackgroundLayer()838 void Screen::closeBackgroundLayer() {
839 debug(2, "CloseBackgroundLayer");
840
841 if (Sword2Engine::isPsx())
842 flushPsxScrCache();
843
844 for (int i = 0; i < MAXLAYERS; i++) {
845 if (_blockSurfaces[i]) {
846 for (int j = 0; j < _xBlocks[i] * _yBlocks[i]; j++)
847 if (_blockSurfaces[i][j])
848 free(_blockSurfaces[i][j]);
849 free(_blockSurfaces[i]);
850 _blockSurfaces[i] = NULL;
851 }
852 }
853
854 _layer = 0;
855 }
856
857 } // End of namespace Sword2
858