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