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 "common/scummsys.h"
24 
25 #include "zvision/zvision.h"
26 #include "zvision/graphics/render_manager.h"
27 #include "zvision/scripting/script_manager.h"
28 #include "zvision/text/text.h"
29 
30 #include "zvision/file/lzss_read_stream.h"
31 
32 #include "common/file.h"
33 #include "common/system.h"
34 #include "common/stream.h"
35 
36 #include "engines/util.h"
37 
38 #include "image/tga.h"
39 
40 namespace ZVision {
41 
RenderManager(ZVision * engine,uint32 windowWidth,uint32 windowHeight,const Common::Rect workingWindow,const Graphics::PixelFormat pixelFormat,bool doubleFPS)42 RenderManager::RenderManager(ZVision *engine, uint32 windowWidth, uint32 windowHeight, const Common::Rect workingWindow, const Graphics::PixelFormat pixelFormat, bool doubleFPS)
43 	: _engine(engine),
44 	  _system(engine->_system),
45 	  _screenCenterX(_workingWindow.width() / 2),
46 	  _screenCenterY(_workingWindow.height() / 2),
47 	  _workingWindow(workingWindow),
48 	  _pixelFormat(pixelFormat),
49 	  _backgroundWidth(0),
50 	  _backgroundHeight(0),
51 	  _backgroundOffset(0),
52 	  _renderTable(_workingWindow.width(), _workingWindow.height()),
53 	  _doubleFPS(doubleFPS),
54 	  _subid(0) {
55 
56 	_backgroundSurface.create(_workingWindow.width(), _workingWindow.height(), _pixelFormat);
57 	_effectSurface.create(_workingWindow.width(), _workingWindow.height(), _pixelFormat);
58 	_warpedSceneSurface.create(_workingWindow.width(), _workingWindow.height(), _pixelFormat);
59 	_menuSurface.create(windowWidth, workingWindow.top, _pixelFormat);
60 
61 	_menuArea = Common::Rect(0, 0, windowWidth, workingWindow.top);
62 
63 	initSubArea(windowWidth, windowHeight, workingWindow);
64 }
65 
~RenderManager()66 RenderManager::~RenderManager() {
67 	_currentBackgroundImage.free();
68 	_backgroundSurface.free();
69 	_effectSurface.free();
70 	_warpedSceneSurface.free();
71 	_menuSurface.free();
72 	_subtitleSurface.free();
73 }
74 
renderSceneToScreen()75 void RenderManager::renderSceneToScreen() {
76 	Graphics::Surface *out = &_warpedSceneSurface;
77 	Graphics::Surface *in = &_backgroundSurface;
78 	Common::Rect outWndDirtyRect;
79 
80 	// If we have graphical effects, we apply them using a temporary buffer
81 	if (!_effects.empty()) {
82 		bool copied = false;
83 		Common::Rect windowRect(_workingWindow.width(), _workingWindow.height());
84 
85 		for (EffectsList::iterator it = _effects.begin(); it != _effects.end(); it++) {
86 			Common::Rect rect = (*it)->getRegion();
87 			Common::Rect screenSpaceLocation = rect;
88 
89 			if ((*it)->isPort()) {
90 				screenSpaceLocation = transformBackgroundSpaceRectToScreenSpace(screenSpaceLocation);
91 			}
92 
93 			if (windowRect.intersects(screenSpaceLocation)) {
94 				if (!copied) {
95 					copied = true;
96 					_effectSurface.copyFrom(_backgroundSurface);
97 					in = &_effectSurface;
98 				}
99 				const Graphics::Surface *post;
100 				if ((*it)->isPort())
101 					post = (*it)->draw(_currentBackgroundImage.getSubArea(rect));
102 				else
103 					post = (*it)->draw(_effectSurface.getSubArea(rect));
104 				Common::Rect empty;
105 				blitSurfaceToSurface(*post, empty, _effectSurface, screenSpaceLocation.left, screenSpaceLocation.top);
106 				screenSpaceLocation.clip(windowRect);
107 				if (_backgroundSurfaceDirtyRect .isEmpty()) {
108 					_backgroundSurfaceDirtyRect = screenSpaceLocation;
109 				} else {
110 					_backgroundSurfaceDirtyRect.extend(screenSpaceLocation);
111 				}
112 			}
113 		}
114 	}
115 
116 	RenderTable::RenderState state = _renderTable.getRenderState();
117 	if (state == RenderTable::PANORAMA || state == RenderTable::TILT) {
118 		if (!_backgroundSurfaceDirtyRect.isEmpty()) {
119 			_renderTable.mutateImage(&_warpedSceneSurface, in);
120 			out = &_warpedSceneSurface;
121 			outWndDirtyRect = Common::Rect(_workingWindow.width(), _workingWindow.height());
122 		}
123 	} else {
124 		out = in;
125 		outWndDirtyRect = _backgroundSurfaceDirtyRect;
126 	}
127 
128 	if (!outWndDirtyRect.isEmpty()) {
129 		Common::Rect rect(
130 			outWndDirtyRect.left + _workingWindow.left,
131 			outWndDirtyRect.top + _workingWindow.top,
132 			outWndDirtyRect.left + _workingWindow.left + outWndDirtyRect.width(),
133 			outWndDirtyRect.top + _workingWindow.top + outWndDirtyRect.height()
134 		);
135 		copyToScreen(*out, rect, outWndDirtyRect.left, outWndDirtyRect.top);
136 	}
137 }
138 
copyToScreen(const Graphics::Surface & surface,Common::Rect & rect,int16 srcLeft,int16 srcTop)139 void RenderManager::copyToScreen(const Graphics::Surface &surface, Common::Rect &rect, int16 srcLeft, int16 srcTop) {
140 	// Convert the surface to RGB565, if needed
141 	Graphics::Surface *outSurface = surface.convertTo(_engine->_screenPixelFormat);
142 	_system->copyRectToScreen(outSurface->getBasePtr(srcLeft, srcTop),
143 		                        outSurface->pitch,
144 		                        rect.left,
145 		                        rect.top,
146 		                        rect.width(),
147 		                        rect.height());
148 	outSurface->free();
149 	delete outSurface;
150 }
151 
renderImageToBackground(const Common::String & fileName,int16 destX,int16 destY)152 void RenderManager::renderImageToBackground(const Common::String &fileName, int16 destX, int16 destY) {
153 	Graphics::Surface surface;
154 	readImageToSurface(fileName, surface);
155 
156 	blitSurfaceToBkg(surface, destX, destY);
157 	surface.free();
158 }
159 
renderImageToBackground(const Common::String & fileName,int16 destX,int16 destY,uint32 keycolor)160 void RenderManager::renderImageToBackground(const Common::String &fileName, int16 destX, int16 destY, uint32 keycolor) {
161 	Graphics::Surface surface;
162 	readImageToSurface(fileName, surface);
163 
164 	blitSurfaceToBkg(surface, destX, destY, keycolor);
165 	surface.free();
166 }
167 
renderImageToBackground(const Common::String & fileName,int16 destX,int16 destY,int16 keyX,int16 keyY)168 void RenderManager::renderImageToBackground(const Common::String &fileName, int16 destX, int16 destY, int16  keyX, int16 keyY) {
169 	Graphics::Surface surface;
170 	readImageToSurface(fileName, surface);
171 
172 	uint16 keycolor = *(uint16 *)surface.getBasePtr(keyX, keyY);
173 
174 	blitSurfaceToBkg(surface, destX, destY, keycolor);
175 	surface.free();
176 }
177 
readImageToSurface(const Common::String & fileName,Graphics::Surface & destination)178 void RenderManager::readImageToSurface(const Common::String &fileName, Graphics::Surface &destination) {
179 	bool isTransposed = _renderTable.getRenderState() == RenderTable::PANORAMA;
180 	readImageToSurface(fileName, destination, isTransposed);
181 }
182 
readImageToSurface(const Common::String & fileName,Graphics::Surface & destination,bool transposed)183 void RenderManager::readImageToSurface(const Common::String &fileName, Graphics::Surface &destination, bool transposed) {
184 	Common::File file;
185 
186 	if (!_engine->getSearchManager()->openFile(file, fileName)) {
187 		warning("Could not open file %s", fileName.c_str());
188 		return;
189 	}
190 
191 	// Read the magic number
192 	// Some files are true TGA, while others are TGZ
193 	uint32 fileType = file.readUint32BE();
194 
195 	int imageWidth;
196 	int imageHeight;
197 	Image::TGADecoder tga;
198 	uint16 *buffer;
199 	// All Z-Vision images are in RGB 555
200 	destination.format = _engine->_resourcePixelFormat;
201 
202 	bool isTGZ;
203 
204 	// Check for TGZ files
205 	if (fileType == MKTAG('T', 'G', 'Z', '\0')) {
206 		isTGZ = true;
207 
208 		// TGZ files have a header and then Bitmap data that is compressed with LZSS
209 		uint32 decompressedSize = file.readSint32LE() / 2;
210 		imageWidth = file.readSint32LE();
211 		imageHeight = file.readSint32LE();
212 
213 		LzssReadStream lzssStream(&file);
214 		buffer = (uint16 *)(new uint16[decompressedSize]);
215 		lzssStream.read(buffer, 2 * decompressedSize);
216 #ifndef SCUMM_LITTLE_ENDIAN
217 		for (uint32 i = 0; i < decompressedSize; ++i)
218 			buffer[i] = FROM_LE_16(buffer[i]);
219 #endif
220 	} else {
221 		isTGZ = false;
222 
223 		// Reset the cursor
224 		file.seek(0);
225 
226 		// Decode
227 		if (!tga.loadStream(file)) {
228 			warning("Error while reading TGA image");
229 			return;
230 		}
231 
232 		Graphics::Surface tgaSurface = *(tga.getSurface());
233 		imageWidth = tgaSurface.w;
234 		imageHeight = tgaSurface.h;
235 
236 		buffer = (uint16 *)tgaSurface.getPixels();
237 	}
238 
239 	// Flip the width and height if transposed
240 	if (transposed) {
241 		SWAP(imageWidth, imageHeight);
242 	}
243 
244 	// If the destination internal buffer is the same size as what we're copying into it,
245 	// there is no need to free() and re-create
246 	if (imageWidth != destination.w || imageHeight != destination.h) {
247 		destination.create(imageWidth, imageHeight, _engine->_resourcePixelFormat);
248 	}
249 
250 	// If transposed, 'un-transpose' the data while copying it to the destination
251 	// Otherwise, just do a simple copy
252 	if (transposed) {
253 		uint16 *dest = (uint16 *)destination.getPixels();
254 
255 		for (int y = 0; y < imageHeight; ++y) {
256 			uint32 columnIndex = y * imageWidth;
257 
258 			for (int x = 0; x < imageWidth; ++x) {
259 				dest[columnIndex + x] = buffer[x * imageHeight + y];
260 			}
261 		}
262 	} else {
263 		memcpy(destination.getPixels(), buffer, imageWidth * imageHeight * destination.format.bytesPerPixel);
264 	}
265 
266 	// Cleanup
267 	if (isTGZ) {
268 		delete[] buffer;
269 	} else {
270 		tga.destroy();
271 	}
272 }
273 
screenSpaceToImageSpace(const Common::Point & point)274 const Common::Point RenderManager::screenSpaceToImageSpace(const Common::Point &point) {
275 	if (_workingWindow.contains(point)) {
276 		// Convert from screen space to working window space
277 		Common::Point newPoint(point - Common::Point(_workingWindow.left, _workingWindow.top));
278 
279 		RenderTable::RenderState state = _renderTable.getRenderState();
280 		if (state == RenderTable::PANORAMA || state == RenderTable::TILT) {
281 			newPoint = _renderTable.convertWarpedCoordToFlatCoord(newPoint);
282 		}
283 
284 		if (state == RenderTable::PANORAMA) {
285 			newPoint += (Common::Point(_backgroundOffset - _screenCenterX, 0));
286 		} else if (state == RenderTable::TILT) {
287 			newPoint += (Common::Point(0, _backgroundOffset - _screenCenterY));
288 		}
289 
290 		if (_backgroundWidth)
291 			newPoint.x %= _backgroundWidth;
292 		if (_backgroundHeight)
293 			newPoint.y %= _backgroundHeight;
294 
295 		if (newPoint.x < 0)
296 			newPoint.x += _backgroundWidth;
297 		if (newPoint.y < 0)
298 			newPoint.y += _backgroundHeight;
299 
300 		return newPoint;
301 	} else {
302 		return Common::Point(0, 0);
303 	}
304 }
305 
getRenderTable()306 RenderTable *RenderManager::getRenderTable() {
307 	return &_renderTable;
308 }
309 
setBackgroundImage(const Common::String & fileName)310 void RenderManager::setBackgroundImage(const Common::String &fileName) {
311 	readImageToSurface(fileName, _currentBackgroundImage);
312 	_backgroundWidth = _currentBackgroundImage.w;
313 	_backgroundHeight = _currentBackgroundImage.h;
314 	_backgroundDirtyRect = Common::Rect(_backgroundWidth, _backgroundHeight);
315 }
316 
setBackgroundPosition(int offset)317 void RenderManager::setBackgroundPosition(int offset) {
318 	RenderTable::RenderState state = _renderTable.getRenderState();
319 	if (state == RenderTable::TILT || state == RenderTable::PANORAMA)
320 		if (_backgroundOffset != offset)
321 			_backgroundDirtyRect = Common::Rect(_backgroundWidth, _backgroundHeight);
322 	_backgroundOffset = offset;
323 
324 	_engine->getScriptManager()->setStateValue(StateKey_ViewPos, offset);
325 }
326 
getCurrentBackgroundOffset()327 uint32 RenderManager::getCurrentBackgroundOffset() {
328 	RenderTable::RenderState state = _renderTable.getRenderState();
329 
330 	if (state == RenderTable::PANORAMA) {
331 		return _backgroundOffset;
332 	} else if (state == RenderTable::TILT) {
333 		return _backgroundOffset;
334 	} else {
335 		return 0;
336 	}
337 }
338 
tranposeSurface(const Graphics::Surface * surface)339 Graphics::Surface *RenderManager::tranposeSurface(const Graphics::Surface *surface) {
340 	Graphics::Surface *tranposedSurface = new Graphics::Surface();
341 	tranposedSurface->create(surface->h, surface->w, surface->format);
342 
343 	const uint16 *source = (const uint16 *)surface->getPixels();
344 	uint16 *dest = (uint16 *)tranposedSurface->getPixels();
345 
346 	for (int y = 0; y < tranposedSurface->h; ++y) {
347 		int columnIndex = y * tranposedSurface->w;
348 
349 		for (int x = 0; x < tranposedSurface->w; ++x) {
350 			dest[columnIndex + x] = source[x * surface->w + y];
351 		}
352 	}
353 
354 	return tranposedSurface;
355 }
356 
scaleBuffer(const void * src,void * dst,uint32 srcWidth,uint32 srcHeight,byte bytesPerPixel,uint32 dstWidth,uint32 dstHeight)357 void RenderManager::scaleBuffer(const void *src, void *dst, uint32 srcWidth, uint32 srcHeight, byte bytesPerPixel, uint32 dstWidth, uint32 dstHeight) {
358 	assert(bytesPerPixel == 1 || bytesPerPixel == 2);
359 
360 	const float  xscale = (float)srcWidth / (float)dstWidth;
361 	const float  yscale = (float)srcHeight / (float)dstHeight;
362 
363 	if (bytesPerPixel == 1) {
364 		const byte *srcPtr = (const byte *)src;
365 		byte *dstPtr = (byte *)dst;
366 		for (uint32 y = 0; y < dstHeight; ++y) {
367 			for (uint32 x = 0; x < dstWidth; ++x) {
368 				*dstPtr = srcPtr[(int)(x * xscale) + (int)(y * yscale) * srcWidth];
369 				dstPtr++;
370 			}
371 		}
372 	} else if (bytesPerPixel == 2) {
373 		const uint16 *srcPtr = (const uint16 *)src;
374 		uint16 *dstPtr = (uint16 *)dst;
375 		for (uint32 y = 0; y < dstHeight; ++y) {
376 			for (uint32 x = 0; x < dstWidth; ++x) {
377 				*dstPtr = srcPtr[(int)(x * xscale) + (int)(y * yscale) * srcWidth];
378 				dstPtr++;
379 			}
380 		}
381 	}
382 }
383 
blitSurfaceToSurface(const Graphics::Surface & src,const Common::Rect & _srcRect,Graphics::Surface & dst,int _x,int _y)384 void RenderManager::blitSurfaceToSurface(const Graphics::Surface &src, const Common::Rect &_srcRect , Graphics::Surface &dst, int _x, int _y) {
385 	Common::Rect srcRect = _srcRect;
386 	if (srcRect.isEmpty())
387 		srcRect = Common::Rect(src.w, src.h);
388 	srcRect.clip(src.w, src.h);
389 	Common::Rect dstRect = Common::Rect(-_x + srcRect.left , -_y + srcRect.top, -_x + srcRect.left + dst.w, -_y + srcRect.top + dst.h);
390 	srcRect.clip(dstRect);
391 
392 	if (srcRect.isEmpty() || !srcRect.isValidRect())
393 		return;
394 
395 	Graphics::Surface *srcAdapted = src.convertTo(dst.format);
396 
397 	// Copy srcRect from src surface to dst surface
398 	const byte *srcBuffer = (const byte *)srcAdapted->getBasePtr(srcRect.left, srcRect.top);
399 
400 	int xx = _x;
401 	int yy = _y;
402 
403 	if (xx < 0)
404 		xx = 0;
405 	if (yy < 0)
406 		yy = 0;
407 
408 	if (_x >= dst.w || _y >= dst.h) {
409 		srcAdapted->free();
410 		delete srcAdapted;
411 		return;
412 	}
413 
414 	byte *dstBuffer = (byte *)dst.getBasePtr(xx, yy);
415 
416 	int32 w = srcRect.width();
417 	int32 h = srcRect.height();
418 
419 	for (int32 y = 0; y < h; y++) {
420 		memcpy(dstBuffer, srcBuffer, w * srcAdapted->format.bytesPerPixel);
421 		srcBuffer += srcAdapted->pitch;
422 		dstBuffer += dst.pitch;
423 	}
424 
425 	srcAdapted->free();
426 	delete srcAdapted;
427 }
428 
blitSurfaceToSurface(const Graphics::Surface & src,const Common::Rect & _srcRect,Graphics::Surface & dst,int _x,int _y,uint32 colorkey)429 void RenderManager::blitSurfaceToSurface(const Graphics::Surface &src, const Common::Rect &_srcRect , Graphics::Surface &dst, int _x, int _y, uint32 colorkey) {
430 	Common::Rect srcRect = _srcRect;
431 	if (srcRect.isEmpty())
432 		srcRect = Common::Rect(src.w, src.h);
433 	srcRect.clip(src.w, src.h);
434 	Common::Rect dstRect = Common::Rect(-_x + srcRect.left , -_y + srcRect.top, -_x + srcRect.left + dst.w, -_y + srcRect.top + dst.h);
435 	srcRect.clip(dstRect);
436 
437 	if (srcRect.isEmpty() || !srcRect.isValidRect())
438 		return;
439 
440 	Graphics::Surface *srcAdapted = src.convertTo(dst.format);
441 	uint32 keycolor = colorkey & ((1 << (src.format.bytesPerPixel << 3)) - 1);
442 
443 	// Copy srcRect from src surface to dst surface
444 	const byte *srcBuffer = (const byte *)srcAdapted->getBasePtr(srcRect.left, srcRect.top);
445 
446 	int xx = _x;
447 	int yy = _y;
448 
449 	if (xx < 0)
450 		xx = 0;
451 	if (yy < 0)
452 		yy = 0;
453 
454 	if (_x >= dst.w || _y >= dst.h) {
455 		srcAdapted->free();
456 		delete srcAdapted;
457 		return;
458 	}
459 
460 	byte *dstBuffer = (byte *)dst.getBasePtr(xx, yy);
461 
462 	int32 w = srcRect.width();
463 	int32 h = srcRect.height();
464 
465 	for (int32 y = 0; y < h; y++) {
466 		switch (srcAdapted->format.bytesPerPixel) {
467 		case 1: {
468 			const uint *srcTemp = (const uint *)srcBuffer;
469 			uint *dstTemp = (uint *)dstBuffer;
470 			for (int32 x = 0; x < w; x++) {
471 				if (*srcTemp != keycolor)
472 					*dstTemp = *srcTemp;
473 				srcTemp++;
474 				dstTemp++;
475 			}
476 		}
477 		break;
478 
479 		case 2: {
480 			const uint16 *srcTemp = (const uint16 *)srcBuffer;
481 			uint16 *dstTemp = (uint16 *)dstBuffer;
482 			for (int32 x = 0; x < w; x++) {
483 				if (*srcTemp != keycolor)
484 					*dstTemp = *srcTemp;
485 				srcTemp++;
486 				dstTemp++;
487 			}
488 		}
489 		break;
490 
491 		case 4: {
492 			const uint32 *srcTemp = (const uint32 *)srcBuffer;
493 			uint32 *dstTemp = (uint32 *)dstBuffer;
494 			for (int32 x = 0; x < w; x++) {
495 				if (*srcTemp != keycolor)
496 					*dstTemp = *srcTemp;
497 				srcTemp++;
498 				dstTemp++;
499 			}
500 		}
501 		break;
502 
503 		default:
504 			break;
505 		}
506 		srcBuffer += srcAdapted->pitch;
507 		dstBuffer += dst.pitch;
508 	}
509 
510 	srcAdapted->free();
511 	delete srcAdapted;
512 }
513 
blitSurfaceToBkg(const Graphics::Surface & src,int x,int y,int32 colorkey)514 void RenderManager::blitSurfaceToBkg(const Graphics::Surface &src, int x, int y, int32 colorkey) {
515 	Common::Rect empt;
516 	if (colorkey >= 0)
517 		blitSurfaceToSurface(src, empt, _currentBackgroundImage, x, y, colorkey);
518 	else
519 		blitSurfaceToSurface(src, empt, _currentBackgroundImage, x, y);
520 	Common::Rect dirty(src.w, src.h);
521 	dirty.translate(x, y);
522 	if (_backgroundDirtyRect.isEmpty())
523 		_backgroundDirtyRect = dirty;
524 	else
525 		_backgroundDirtyRect.extend(dirty);
526 }
527 
blitSurfaceToBkgScaled(const Graphics::Surface & src,const Common::Rect & _dstRect,int32 colorkey)528 void RenderManager::blitSurfaceToBkgScaled(const Graphics::Surface &src, const Common::Rect &_dstRect, int32 colorkey) {
529 	if (src.w == _dstRect.width() && src.h == _dstRect.height()) {
530 		blitSurfaceToBkg(src, _dstRect.left, _dstRect.top, colorkey);
531 	} else {
532 		Graphics::Surface *tmp = new Graphics::Surface;
533 		tmp->create(_dstRect.width(), _dstRect.height(), src.format);
534 		scaleBuffer(src.getPixels(), tmp->getPixels(), src.w, src.h, src.format.bytesPerPixel, _dstRect.width(), _dstRect.height());
535 		blitSurfaceToBkg(*tmp, _dstRect.left, _dstRect.top, colorkey);
536 		tmp->free();
537 		delete tmp;
538 	}
539 }
540 
blitSurfaceToMenu(const Graphics::Surface & src,int x,int y,int32 colorkey)541 void RenderManager::blitSurfaceToMenu(const Graphics::Surface &src, int x, int y, int32 colorkey) {
542 	Common::Rect empt;
543 	if (colorkey >= 0)
544 		blitSurfaceToSurface(src, empt, _menuSurface, x, y, colorkey);
545 	else
546 		blitSurfaceToSurface(src, empt, _menuSurface, x, y);
547 	Common::Rect dirty(src.w, src.h);
548 	dirty.translate(x, y);
549 	if (_menuSurfaceDirtyRect.isEmpty())
550 		_menuSurfaceDirtyRect = dirty;
551 	else
552 		_menuSurfaceDirtyRect.extend(dirty);
553 }
554 
getBkgRect(Common::Rect & rect)555 Graphics::Surface *RenderManager::getBkgRect(Common::Rect &rect) {
556 	Common::Rect dst = rect;
557 	dst.clip(_backgroundWidth, _backgroundHeight);
558 
559 	if (dst.isEmpty() || !dst.isValidRect())
560 		return NULL;
561 
562 	Graphics::Surface *srf = new Graphics::Surface;
563 	srf->create(dst.width(), dst.height(), _currentBackgroundImage.format);
564 
565 	srf->copyRectToSurface(_currentBackgroundImage, 0, 0, Common::Rect(dst));
566 
567 	return srf;
568 }
569 
loadImage(Common::String file)570 Graphics::Surface *RenderManager::loadImage(Common::String file) {
571 	Graphics::Surface *tmp = new Graphics::Surface;
572 	readImageToSurface(file, *tmp);
573 	return tmp;
574 }
575 
loadImage(Common::String file,bool transposed)576 Graphics::Surface *RenderManager::loadImage(Common::String file, bool transposed) {
577 	Graphics::Surface *tmp = new Graphics::Surface;
578 	readImageToSurface(file, *tmp, transposed);
579 	return tmp;
580 }
581 
prepareBackground()582 void RenderManager::prepareBackground() {
583 	_backgroundDirtyRect.clip(_backgroundWidth, _backgroundHeight);
584 	RenderTable::RenderState state = _renderTable.getRenderState();
585 
586 	if (state == RenderTable::PANORAMA) {
587 		// Calculate the visible portion of the background
588 		Common::Rect viewPort(_workingWindow.width(), _workingWindow.height());
589 		viewPort.translate(-(_screenCenterX - _backgroundOffset), 0);
590 		Common::Rect drawRect = _backgroundDirtyRect;
591 		drawRect.clip(viewPort);
592 
593 		// Render the visible portion
594 		if (!drawRect.isEmpty()) {
595 			blitSurfaceToSurface(_currentBackgroundImage, drawRect, _backgroundSurface, _screenCenterX - _backgroundOffset + drawRect.left, drawRect.top);
596 		}
597 
598 		// Mark the dirty portion of the surface
599 		_backgroundSurfaceDirtyRect = _backgroundDirtyRect;
600 		_backgroundSurfaceDirtyRect.translate(_screenCenterX - _backgroundOffset, 0);
601 
602 		// Panorama mode allows the user to spin in circles. Therefore, we need to render
603 		// the portion of the image that wrapped to the other side of the screen
604 		if (_backgroundOffset < _screenCenterX) {
605 			viewPort.moveTo(-(_screenCenterX - (_backgroundOffset + _backgroundWidth)), 0);
606 			drawRect = _backgroundDirtyRect;
607 			drawRect.clip(viewPort);
608 
609 			if (!drawRect.isEmpty())
610 				blitSurfaceToSurface(_currentBackgroundImage, drawRect, _backgroundSurface, _screenCenterX - (_backgroundOffset + _backgroundWidth) + drawRect.left, drawRect.top);
611 
612 			Common::Rect tmp = _backgroundDirtyRect;
613 			tmp.translate(_screenCenterX - (_backgroundOffset + _backgroundWidth), 0);
614 			if (!tmp.isEmpty())
615 				_backgroundSurfaceDirtyRect.extend(tmp);
616 
617 		} else if (_backgroundWidth - _backgroundOffset < _screenCenterX) {
618 			viewPort.moveTo(-(_screenCenterX + _backgroundWidth - _backgroundOffset), 0);
619 			drawRect = _backgroundDirtyRect;
620 			drawRect.clip(viewPort);
621 
622 			if (!drawRect.isEmpty())
623 				blitSurfaceToSurface(_currentBackgroundImage, drawRect, _backgroundSurface, _screenCenterX + _backgroundWidth - _backgroundOffset + drawRect.left, drawRect.top);
624 
625 			Common::Rect tmp = _backgroundDirtyRect;
626 			tmp.translate(_screenCenterX + _backgroundWidth - _backgroundOffset, 0);
627 			if (!tmp.isEmpty())
628 				_backgroundSurfaceDirtyRect.extend(tmp);
629 
630 		}
631 	} else if (state == RenderTable::TILT) {
632 		// Tilt doesn't allow wrapping, so we just do a simple clip
633 		Common::Rect viewPort(_workingWindow.width(), _workingWindow.height());
634 		viewPort.translate(0, -(_screenCenterY - _backgroundOffset));
635 		Common::Rect drawRect = _backgroundDirtyRect;
636 		drawRect.clip(viewPort);
637 		if (!drawRect.isEmpty())
638 			blitSurfaceToSurface(_currentBackgroundImage, drawRect, _backgroundSurface, drawRect.left, _screenCenterY - _backgroundOffset + drawRect.top);
639 
640 		// Mark the dirty portion of the surface
641 		_backgroundSurfaceDirtyRect = _backgroundDirtyRect;
642 		_backgroundSurfaceDirtyRect.translate(0, _screenCenterY - _backgroundOffset);
643 
644 	} else {
645 		if (!_backgroundDirtyRect.isEmpty())
646 			blitSurfaceToSurface(_currentBackgroundImage, _backgroundDirtyRect, _backgroundSurface, _backgroundDirtyRect.left, _backgroundDirtyRect.top);
647 		_backgroundSurfaceDirtyRect = _backgroundDirtyRect;
648 	}
649 
650 	// Clear the dirty rect since everything is clean now
651 	_backgroundDirtyRect = Common::Rect();
652 
653 	_backgroundSurfaceDirtyRect.clip(_workingWindow.width(), _workingWindow.height());
654 }
655 
clearMenuSurface()656 void RenderManager::clearMenuSurface() {
657 	_menuSurfaceDirtyRect = Common::Rect(0, 0, _menuSurface.w, _menuSurface.h);
658 	_menuSurface.fillRect(_menuSurfaceDirtyRect, 0);
659 }
660 
clearMenuSurface(const Common::Rect & r)661 void RenderManager::clearMenuSurface(const Common::Rect &r) {
662 	if (_menuSurfaceDirtyRect.isEmpty())
663 		_menuSurfaceDirtyRect = r;
664 	else
665 		_menuSurfaceDirtyRect.extend(r);
666 	_menuSurface.fillRect(r, 0);
667 }
668 
renderMenuToScreen()669 void RenderManager::renderMenuToScreen() {
670 	if (!_menuSurfaceDirtyRect.isEmpty()) {
671 		_menuSurfaceDirtyRect.clip(Common::Rect(_menuSurface.w, _menuSurface.h));
672 		if (!_menuSurfaceDirtyRect.isEmpty()) {
673 			Common::Rect rect(
674 				_menuSurfaceDirtyRect.left + _menuArea.left,
675 				_menuSurfaceDirtyRect.top + _menuArea.top,
676 				_menuSurfaceDirtyRect.left + _menuArea.left + _menuSurfaceDirtyRect.width(),
677 				_menuSurfaceDirtyRect.top + _menuArea.top + _menuSurfaceDirtyRect.height()
678 			);
679 			copyToScreen(_menuSurface, rect, _menuSurfaceDirtyRect.left, _menuSurfaceDirtyRect.top);
680 		}
681 		_menuSurfaceDirtyRect = Common::Rect();
682 	}
683 }
684 
initSubArea(uint32 windowWidth,uint32 windowHeight,const Common::Rect workingWindow)685 void RenderManager::initSubArea(uint32 windowWidth, uint32 windowHeight, const Common::Rect workingWindow) {
686 	_workingWindow = workingWindow;
687 
688 	_subtitleSurface.free();
689 
690 	_subtitleSurface.create(windowWidth, windowHeight - workingWindow.bottom, _pixelFormat);
691 	_subtitleArea = Common::Rect(0, workingWindow.bottom, windowWidth, windowHeight);
692 }
693 
createSubArea(const Common::Rect & area)694 uint16 RenderManager::createSubArea(const Common::Rect &area) {
695 	_subid++;
696 
697 	OneSubtitle sub;
698 	sub.redraw = false;
699 	sub.timer = -1;
700 	sub.todelete = false;
701 	sub.r = area;
702 
703 	_subsList[_subid] = sub;
704 
705 	return _subid;
706 }
707 
createSubArea()708 uint16 RenderManager::createSubArea() {
709 	Common::Rect r(_subtitleArea.left, _subtitleArea.top, _subtitleArea.right, _subtitleArea.bottom);
710 	r.translate(-_workingWindow.left, -_workingWindow.top);
711 	return createSubArea(r);
712 }
713 
deleteSubArea(uint16 id)714 void RenderManager::deleteSubArea(uint16 id) {
715 	if (_subsList.contains(id))
716 		_subsList[id].todelete = true;
717 }
718 
deleteSubArea(uint16 id,int16 delay)719 void RenderManager::deleteSubArea(uint16 id, int16 delay) {
720 	if (_subsList.contains(id))
721 		_subsList[id].timer = delay;
722 }
723 
updateSubArea(uint16 id,const Common::String & txt)724 void RenderManager::updateSubArea(uint16 id, const Common::String &txt) {
725 	if (_subsList.contains(id)) {
726 		OneSubtitle *sub = &_subsList[id];
727 		sub->txt = txt;
728 		sub->redraw = true;
729 	}
730 }
731 
processSubs(uint16 deltatime)732 void RenderManager::processSubs(uint16 deltatime) {
733 	bool redraw = false;
734 	for (SubtitleMap::iterator it = _subsList.begin(); it != _subsList.end(); it++) {
735 		if (it->_value.timer != -1) {
736 			it->_value.timer -= deltatime;
737 			if (it->_value.timer <= 0)
738 				it->_value.todelete = true;
739 		}
740 		if (it->_value.todelete) {
741 			_subsList.erase(it);
742 			redraw = true;
743 		} else if (it->_value.redraw) {
744 			redraw = true;
745 		}
746 	}
747 
748 	if (redraw) {
749 		_subtitleSurface.fillRect(Common::Rect(_subtitleSurface.w, _subtitleSurface.h), 0);
750 
751 		for (SubtitleMap::iterator it = _subsList.begin(); it != _subsList.end(); it++) {
752 			OneSubtitle *sub = &it->_value;
753 			if (sub->txt.size()) {
754 				Graphics::Surface subtitleSurface;
755 				subtitleSurface.create(sub->r.width(), sub->r.height(), _engine->_resourcePixelFormat);
756 				_engine->getTextRenderer()->drawTextWithWordWrapping(sub->txt, subtitleSurface);
757 				Common::Rect empty;
758 				blitSurfaceToSurface(subtitleSurface, empty, _subtitleSurface, sub->r.left - _subtitleArea.left + _workingWindow.left, sub->r.top - _subtitleArea.top + _workingWindow.top);
759 				subtitleSurface.free();
760 			}
761 			sub->redraw = false;
762 		}
763 
764 		Common::Rect rect(
765 			_subtitleArea.left,
766 			_subtitleArea.top,
767 			_subtitleArea.left + _subtitleSurface.w,
768 			_subtitleArea.top + _subtitleSurface.h
769 		);
770 		copyToScreen(_subtitleSurface, rect, 0, 0);
771 	}
772 }
773 
getBkgSize()774 Common::Point RenderManager::getBkgSize() {
775 	return Common::Point(_backgroundWidth, _backgroundHeight);
776 }
777 
addEffect(GraphicsEffect * _effect)778 void RenderManager::addEffect(GraphicsEffect *_effect) {
779 	_effects.push_back(_effect);
780 }
781 
deleteEffect(uint32 ID)782 void RenderManager::deleteEffect(uint32 ID) {
783 	for (EffectsList::iterator it = _effects.begin(); it != _effects.end(); it++) {
784 		if ((*it)->getKey() == ID) {
785 			delete *it;
786 			it = _effects.erase(it);
787 		}
788 	}
789 }
790 
transformBackgroundSpaceRectToScreenSpace(const Common::Rect & src)791 Common::Rect RenderManager::transformBackgroundSpaceRectToScreenSpace(const Common::Rect &src) {
792 	Common::Rect tmp = src;
793 	RenderTable::RenderState state = _renderTable.getRenderState();
794 
795 	if (state == RenderTable::PANORAMA) {
796 		if (_backgroundOffset < _screenCenterX) {
797 			Common::Rect rScreen(_screenCenterX + _backgroundOffset, _workingWindow.height());
798 			Common::Rect lScreen(_workingWindow.width() - rScreen.width(), _workingWindow.height());
799 			lScreen.translate(_backgroundWidth - lScreen.width(), 0);
800 			lScreen.clip(src);
801 			rScreen.clip(src);
802 			if (rScreen.width() < lScreen.width()) {
803 				tmp.translate(_screenCenterX - _backgroundOffset - _backgroundWidth, 0);
804 			} else {
805 				tmp.translate(_screenCenterX - _backgroundOffset, 0);
806 			}
807 		} else if (_backgroundWidth - _backgroundOffset < _screenCenterX) {
808 			Common::Rect rScreen(_screenCenterX - (_backgroundWidth - _backgroundOffset), _workingWindow.height());
809 			Common::Rect lScreen(_workingWindow.width() - rScreen.width(), _workingWindow.height());
810 			lScreen.translate(_backgroundWidth - lScreen.width(), 0);
811 			lScreen.clip(src);
812 			rScreen.clip(src);
813 			if (lScreen.width() < rScreen.width()) {
814 				tmp.translate(_screenCenterX + (_backgroundWidth - _backgroundOffset), 0);
815 			} else {
816 				tmp.translate(_screenCenterX - _backgroundOffset, 0);
817 			}
818 		} else {
819 			tmp.translate(_screenCenterX - _backgroundOffset, 0);
820 		}
821 	} else if (state == RenderTable::TILT) {
822 		tmp.translate(0, (_screenCenterY - _backgroundOffset));
823 	}
824 
825 	return tmp;
826 }
827 
makeEffectMap(const Common::Point & xy,int16 depth,const Common::Rect & rect,int8 * _minComp,int8 * _maxComp)828 EffectMap *RenderManager::makeEffectMap(const Common::Point &xy, int16 depth, const Common::Rect &rect, int8 *_minComp, int8 *_maxComp) {
829 	Common::Rect bkgRect(_backgroundWidth, _backgroundHeight);
830 	if (!bkgRect.contains(xy))
831 		return NULL;
832 
833 	if (!bkgRect.intersects(rect))
834 		return NULL;
835 
836 	uint16 color = *(uint16 *)_currentBackgroundImage.getBasePtr(xy.x, xy.y);
837 	uint8 stC1, stC2, stC3;
838 	_currentBackgroundImage.format.colorToRGB(color, stC1, stC2, stC3);
839 	EffectMap *newMap = new EffectMap;
840 
841 	EffectMapUnit unit;
842 	unit.count = 0;
843 	unit.inEffect = false;
844 
845 	int16 w = rect.width();
846 	int16 h = rect.height();
847 
848 	bool first = true;
849 
850 	uint8 minComp = MIN(MIN(stC1, stC2), stC3);
851 	uint8 maxComp = MAX(MAX(stC1, stC2), stC3);
852 
853 	uint8 depth8 = depth << 3;
854 
855 	for (int16 j = 0; j < h; j++) {
856 		uint16 *pix = (uint16 *)_currentBackgroundImage.getBasePtr(rect.left, rect.top + j);
857 		for (int16 i = 0; i < w; i++) {
858 			uint16 curClr = pix[i];
859 			uint8 cC1, cC2, cC3;
860 			_currentBackgroundImage.format.colorToRGB(curClr, cC1, cC2, cC3);
861 
862 			bool use = false;
863 
864 			if (curClr == color)
865 				use = true;
866 			else if (curClr > color) {
867 				if ((cC1 - stC1 < depth8) &&
868 				        (cC2 - stC2 < depth8) &&
869 				        (cC3 - stC3 < depth8))
870 					use = true;
871 			} else { /* if (curClr < color) */
872 				if ((stC1 - cC1 < depth8) &&
873 				        (stC2 - cC2 < depth8) &&
874 				        (stC3 - cC3 < depth8))
875 					use = true;
876 			}
877 
878 			if (first) {
879 				unit.inEffect = use;
880 				first = false;
881 			}
882 
883 			if (use) {
884 				uint8 cMinComp = MIN(MIN(cC1, cC2), cC3);
885 				uint8 cMaxComp = MAX(MAX(cC1, cC2), cC3);
886 				if (cMinComp < minComp)
887 					minComp = cMinComp;
888 				if (cMaxComp > maxComp)
889 					maxComp = cMaxComp;
890 			}
891 
892 			if (unit.inEffect == use)
893 				unit.count++;
894 			else {
895 				newMap->push_back(unit);
896 				unit.count = 1;
897 				unit.inEffect = use;
898 			}
899 		}
900 	}
901 	newMap->push_back(unit);
902 
903 	if (_minComp) {
904 		if (minComp - depth8 < 0)
905 			*_minComp = -(minComp >> 3);
906 		else
907 			*_minComp = -depth;
908 	}
909 	if (_maxComp) {
910 		if ((int16)maxComp + (int16)depth8 > 255)
911 			*_maxComp = (255 - maxComp) >> 3;
912 		else
913 			*_maxComp = depth;
914 	}
915 
916 	return newMap;
917 }
918 
makeEffectMap(const Graphics::Surface & surf,uint16 transp)919 EffectMap *RenderManager::makeEffectMap(const Graphics::Surface &surf, uint16 transp) {
920 	EffectMapUnit unit;
921 	unit.count = 0;
922 	unit.inEffect = false;
923 
924 	int16 w = surf.w;
925 	int16 h = surf.h;
926 
927 	EffectMap *newMap = new EffectMap;
928 
929 	bool first = true;
930 
931 	for (int16 j = 0; j < h; j++) {
932 		const uint16 *pix = (const uint16 *)surf.getBasePtr(0, j);
933 		for (int16 i = 0; i < w; i++) {
934 			bool use = false;
935 			if (pix[i] != transp)
936 				use = true;
937 
938 			if (first) {
939 				unit.inEffect = use;
940 				first = false;
941 			}
942 
943 			if (unit.inEffect == use)
944 				unit.count++;
945 			else {
946 				newMap->push_back(unit);
947 				unit.count = 1;
948 				unit.inEffect = use;
949 			}
950 		}
951 	}
952 	newMap->push_back(unit);
953 
954 	return newMap;
955 }
956 
markDirty()957 void RenderManager::markDirty() {
958 	_backgroundDirtyRect = Common::Rect(_backgroundWidth, _backgroundHeight);
959 }
960 
961 #if 0
962 void RenderManager::bkgFill(uint8 r, uint8 g, uint8 b) {
963 	_currentBackgroundImage.fillRect(Common::Rect(_currentBackgroundImage.w, _currentBackgroundImage.h), _currentBackgroundImage.format.RGBToColor(r, g, b));
964 	markDirty();
965 }
966 #endif
967 
timedMessage(const Common::String & str,uint16 milsecs)968 void RenderManager::timedMessage(const Common::String &str, uint16 milsecs) {
969 	uint16 msgid = createSubArea();
970 	updateSubArea(msgid, str);
971 	deleteSubArea(msgid, milsecs);
972 }
973 
askQuestion(const Common::String & str)974 bool RenderManager::askQuestion(const Common::String &str) {
975 	Graphics::Surface textSurface;
976 	textSurface.create(_subtitleArea.width(), _subtitleArea.height(), _engine->_resourcePixelFormat);
977 	_engine->getTextRenderer()->drawTextWithWordWrapping(str, textSurface);
978 	copyToScreen(textSurface, _subtitleArea, 0, 0);
979 
980 	_engine->stopClock();
981 
982 	int result = 0;
983 
984 	while (result == 0) {
985 		Common::Event evnt;
986 		while (_engine->getEventManager()->pollEvent(evnt)) {
987 			if (evnt.type == Common::EVENT_KEYDOWN) {
988 				// English: yes/no
989 				// German: ja/nein
990 				// Spanish: si/no
991 				// French Nemesis: F4/any other key
992 				// French ZGI: oui/non
993 				// TODO: Handle this using the keymapper
994 				switch (evnt.kbd.keycode) {
995 				case Common::KEYCODE_y:
996 					if (_engine->getLanguage() == Common::EN_ANY)
997 						result = 2;
998 					break;
999 				case Common::KEYCODE_j:
1000 					if (_engine->getLanguage() == Common::DE_DEU)
1001 						result = 2;
1002 					break;
1003 				case Common::KEYCODE_s:
1004 					if (_engine->getLanguage() == Common::ES_ESP)
1005 						result = 2;
1006 					break;
1007 				case Common::KEYCODE_o:
1008 					if (_engine->getLanguage() == Common::FR_FRA && _engine->getGameId() == GID_GRANDINQUISITOR)
1009 						result = 2;
1010 					break;
1011 				case Common::KEYCODE_F4:
1012 					if (_engine->getLanguage() == Common::FR_FRA && _engine->getGameId() == GID_NEMESIS)
1013 						result = 2;
1014 					break;
1015 				case Common::KEYCODE_n:
1016 					result = 1;
1017 					break;
1018 				default:
1019 					if (_engine->getLanguage() == Common::FR_FRA && _engine->getGameId() == GID_NEMESIS)
1020 						result = 1;
1021 					break;
1022 				}
1023 			}
1024 		}
1025 		_system->updateScreen();
1026 		if (_doubleFPS)
1027 			_system->delayMillis(33);
1028 		else
1029 			_system->delayMillis(66);
1030 	}
1031 
1032 	// Draw over the text in order to clear it
1033 	textSurface.fillRect(Common::Rect(_subtitleArea.width(), _subtitleArea.height()), 0);
1034 	copyToScreen(textSurface, _subtitleArea, 0, 0);
1035 
1036 	// Free the surface
1037 	textSurface.free();
1038 
1039 	_engine->startClock();
1040 	return result == 2;
1041 }
1042 
delayedMessage(const Common::String & str,uint16 milsecs)1043 void RenderManager::delayedMessage(const Common::String &str, uint16 milsecs) {
1044 	uint16 msgid = createSubArea();
1045 	updateSubArea(msgid, str);
1046 	processSubs(0);
1047 	renderSceneToScreen();
1048 	_engine->stopClock();
1049 
1050 	uint32 stopTime = _system->getMillis() + milsecs;
1051 	while (_system->getMillis() < stopTime) {
1052 		Common::Event evnt;
1053 		while (_engine->getEventManager()->pollEvent(evnt)) {
1054 			if (evnt.type == Common::EVENT_KEYDOWN &&
1055 			        (evnt.kbd.keycode == Common::KEYCODE_SPACE ||
1056 			         evnt.kbd.keycode == Common::KEYCODE_RETURN ||
1057 			         evnt.kbd.keycode == Common::KEYCODE_ESCAPE))
1058 				break;
1059 		}
1060 		_system->updateScreen();
1061 		if (_doubleFPS)
1062 			_system->delayMillis(33);
1063 		else
1064 			_system->delayMillis(66);
1065 	}
1066 	deleteSubArea(msgid);
1067 	_engine->startClock();
1068 }
1069 
showDebugMsg(const Common::String & msg,int16 delay)1070 void RenderManager::showDebugMsg(const Common::String &msg, int16 delay) {
1071 	uint16 msgid = createSubArea();
1072 	updateSubArea(msgid, msg);
1073 	deleteSubArea(msgid, delay);
1074 }
1075 
updateRotation()1076 void RenderManager::updateRotation() {
1077 	int16 _velocity = _engine->getMouseVelocity() + _engine->getKeyboardVelocity();
1078 	ScriptManager *scriptManager = _engine->getScriptManager();
1079 
1080 	if (_doubleFPS)
1081 		_velocity /= 2;
1082 
1083 	if (_velocity) {
1084 		RenderTable::RenderState renderState = _renderTable.getRenderState();
1085 		if (renderState == RenderTable::PANORAMA) {
1086 			int16 startPosition = scriptManager->getStateValue(StateKey_ViewPos);
1087 
1088 			int16 newPosition = startPosition + (_renderTable.getPanoramaReverse() ? -_velocity : _velocity);
1089 
1090 			int16 zeroPoint = _renderTable.getPanoramaZeroPoint();
1091 			if (startPosition >= zeroPoint && newPosition < zeroPoint)
1092 				scriptManager->setStateValue(StateKey_Rounds, scriptManager->getStateValue(StateKey_Rounds) - 1);
1093 			if (startPosition <= zeroPoint && newPosition > zeroPoint)
1094 				scriptManager->setStateValue(StateKey_Rounds, scriptManager->getStateValue(StateKey_Rounds) + 1);
1095 
1096 			int16 screenWidth = getBkgSize().x;
1097 			if (screenWidth)
1098 				newPosition %= screenWidth;
1099 
1100 			if (newPosition < 0)
1101 				newPosition += screenWidth;
1102 
1103 			setBackgroundPosition(newPosition);
1104 		} else if (renderState == RenderTable::TILT) {
1105 			int16 startPosition = scriptManager->getStateValue(StateKey_ViewPos);
1106 
1107 			int16 newPosition = startPosition + _velocity;
1108 
1109 			int16 screenHeight = getBkgSize().y;
1110 			int16 tiltGap = (int16)_renderTable.getTiltGap();
1111 
1112 			if (newPosition >= (screenHeight - tiltGap))
1113 				newPosition = screenHeight - tiltGap;
1114 			if (newPosition <= tiltGap)
1115 				newPosition = tiltGap;
1116 
1117 			setBackgroundPosition(newPosition);
1118 		}
1119 	}
1120 }
1121 
checkBorders()1122 void RenderManager::checkBorders() {
1123 	RenderTable::RenderState renderState = _renderTable.getRenderState();
1124 	if (renderState == RenderTable::PANORAMA) {
1125 		int16 startPosition = _engine->getScriptManager()->getStateValue(StateKey_ViewPos);
1126 
1127 		int16 newPosition = startPosition;
1128 
1129 		int16 screenWidth = getBkgSize().x;
1130 
1131 		if (screenWidth)
1132 			newPosition %= screenWidth;
1133 
1134 		if (newPosition < 0)
1135 			newPosition += screenWidth;
1136 
1137 		if (startPosition != newPosition)
1138 			setBackgroundPosition(newPosition);
1139 	} else if (renderState == RenderTable::TILT) {
1140 		int16 startPosition = _engine->getScriptManager()->getStateValue(StateKey_ViewPos);
1141 
1142 		int16 newPosition = startPosition;
1143 
1144 		int16 screenHeight = getBkgSize().y;
1145 		int16 tiltGap = (int16)_renderTable.getTiltGap();
1146 
1147 		if (newPosition >= (screenHeight - tiltGap))
1148 			newPosition = screenHeight - tiltGap;
1149 		if (newPosition <= tiltGap)
1150 			newPosition = tiltGap;
1151 
1152 		if (startPosition != newPosition)
1153 			setBackgroundPosition(newPosition);
1154 	}
1155 }
1156 
rotateTo(int16 _toPos,int16 _time)1157 void RenderManager::rotateTo(int16 _toPos, int16 _time) {
1158 	if (_renderTable.getRenderState() != RenderTable::PANORAMA)
1159 		return;
1160 
1161 	if (_time == 0)
1162 		_time = 1;
1163 
1164 	int32 maxX = getBkgSize().x;
1165 	int32 curX = getCurrentBackgroundOffset();
1166 	int32 dx = 0;
1167 
1168 	if (curX == _toPos)
1169 		return;
1170 
1171 	if (curX > _toPos) {
1172 		if (curX - _toPos > maxX / 2)
1173 			dx = (_toPos + (maxX - curX)) / _time;
1174 		else
1175 			dx = -(curX - _toPos) / _time;
1176 	} else {
1177 		if (_toPos - curX > maxX / 2)
1178 			dx = -((maxX - _toPos) + curX) / _time;
1179 		else
1180 			dx = (_toPos - curX) / _time;
1181 	}
1182 
1183 	_engine->stopClock();
1184 
1185 	for (int16 i = 0; i <= _time; i++) {
1186 		if (i == _time)
1187 			curX = _toPos;
1188 		else
1189 			curX += dx;
1190 
1191 		if (curX < 0)
1192 			curX = maxX - curX;
1193 		else if (curX >= maxX)
1194 			curX %= maxX;
1195 
1196 		setBackgroundPosition(curX);
1197 
1198 		prepareBackground();
1199 		renderSceneToScreen();
1200 
1201 		_system->updateScreen();
1202 
1203 		_system->delayMillis(500 / _time);
1204 	}
1205 
1206 	_engine->startClock();
1207 }
1208 
upscaleRect(Common::Rect & rect)1209 void RenderManager::upscaleRect(Common::Rect &rect) {
1210 	rect.top = rect.top * HIRES_WINDOW_HEIGHT / WINDOW_HEIGHT;
1211 	rect.left = rect.left * HIRES_WINDOW_WIDTH / WINDOW_WIDTH;
1212 	rect.bottom = rect.bottom * HIRES_WINDOW_HEIGHT / WINDOW_HEIGHT;
1213 	rect.right = rect.right * HIRES_WINDOW_WIDTH / WINDOW_WIDTH;
1214 }
1215 
1216 } // End of namespace ZVision
1217