PageRenderTime 40ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 1ms

/engines/neverhood/screen.cpp

http://github.com/scummvm/scummvm
C++ | 422 lines | 321 code | 79 blank | 22 comment | 75 complexity | 86b3f86676c4f96fa199181b1ed684d8 MD5 | raw file
Possible License(s): GPL-3.0, LGPL-2.1, GPL-2.0
  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. #include "graphics/palette.h"
  23. #include "video/smk_decoder.h"
  24. #include "neverhood/screen.h"
  25. namespace Neverhood {
  26. Screen::Screen(NeverhoodEngine *vm)
  27. : _vm(vm), _paletteData(NULL), _paletteChanged(false), _smackerDecoder(NULL),
  28. _yOffset(0), _fullRefresh(false), _frameDelay(0), _savedSmackerDecoder(NULL),
  29. _savedFrameDelay(0), _savedYOffset(0) {
  30. _ticks = _vm->_system->getMillis();
  31. _backScreen = new Graphics::Surface();
  32. _backScreen->create(640, 480, Graphics::PixelFormat::createFormatCLUT8());
  33. _renderQueue = new RenderQueue();
  34. _prevRenderQueue = new RenderQueue();
  35. _microTiles = new MicroTileArray(640, 480);
  36. }
  37. Screen::~Screen() {
  38. delete _microTiles;
  39. delete _renderQueue;
  40. delete _prevRenderQueue;
  41. _backScreen->free();
  42. delete _backScreen;
  43. }
  44. void Screen::update() {
  45. _ticks = _vm->_system->getMillis();
  46. updatePalette();
  47. if (_fullRefresh) {
  48. // NOTE When playing a fullscreen/doubled Smacker video usually a full screen refresh is needed
  49. _vm->_system->copyRectToScreen((const byte*)_backScreen->getPixels(), _backScreen->pitch, 0, 0, 640, 480);
  50. _fullRefresh = false;
  51. return;
  52. }
  53. _microTiles->clear();
  54. for (RenderQueue::iterator it = _renderQueue->begin(); it != _renderQueue->end(); ++it) {
  55. RenderItem &renderItem = (*it);
  56. renderItem._refresh = true;
  57. for (RenderQueue::iterator jt = _prevRenderQueue->begin(); jt != _prevRenderQueue->end(); ++jt) {
  58. RenderItem &prevRenderItem = (*jt);
  59. if (prevRenderItem == renderItem) {
  60. prevRenderItem._refresh = false;
  61. renderItem._refresh = false;
  62. }
  63. }
  64. }
  65. for (RenderQueue::iterator jt = _prevRenderQueue->begin(); jt != _prevRenderQueue->end(); ++jt) {
  66. RenderItem &prevRenderItem = (*jt);
  67. if (prevRenderItem._refresh)
  68. _microTiles->addRect(Common::Rect(prevRenderItem._destX, prevRenderItem._destY, prevRenderItem._destX + prevRenderItem._width, prevRenderItem._destY + prevRenderItem._height));
  69. }
  70. for (RenderQueue::iterator it = _renderQueue->begin(); it != _renderQueue->end(); ++it) {
  71. RenderItem &renderItem = (*it);
  72. if (renderItem._refresh)
  73. _microTiles->addRect(Common::Rect(renderItem._destX, renderItem._destY, renderItem._destX + renderItem._width, renderItem._destY + renderItem._height));
  74. renderItem._refresh = true;
  75. }
  76. RectangleList *updateRects = _microTiles->getRectangles();
  77. for (RenderQueue::iterator it = _renderQueue->begin(); it != _renderQueue->end(); ++it) {
  78. RenderItem &renderItem = (*it);
  79. for (RectangleList::iterator ri = updateRects->begin(); ri != updateRects->end(); ++ri)
  80. blitRenderItem(renderItem, *ri);
  81. }
  82. SWAP(_renderQueue, _prevRenderQueue);
  83. _renderQueue->clear();
  84. for (RectangleList::iterator ri = updateRects->begin(); ri != updateRects->end(); ++ri) {
  85. Common::Rect &r = *ri;
  86. _vm->_system->copyRectToScreen((const byte*)_backScreen->getBasePtr(r.left, r.top), _backScreen->pitch, r.left, r.top, r.width(), r.height());
  87. }
  88. delete updateRects;
  89. }
  90. uint32 Screen::getNextFrameTime() {
  91. int32 frameDelay = _frameDelay;
  92. if (_smackerDecoder && _smackerDecoder->isVideoLoaded() && !_smackerDecoder->endOfVideo())
  93. frameDelay = _smackerDecoder->getTimeToNextFrame();
  94. int32 waitTicks = frameDelay - (_vm->_system->getMillis() - _ticks);
  95. return _vm->_system->getMillis() + waitTicks;
  96. }
  97. void Screen::saveParams() {
  98. _savedSmackerDecoder = _smackerDecoder;
  99. _savedFrameDelay = _frameDelay;
  100. _savedYOffset = _yOffset;
  101. }
  102. void Screen::restoreParams() {
  103. _smackerDecoder = _savedSmackerDecoder;
  104. _frameDelay = _savedFrameDelay;
  105. _yOffset = _savedYOffset;
  106. }
  107. void Screen::setFps(int fps) {
  108. _frameDelay = 1000 / fps;
  109. }
  110. int Screen::getFps() {
  111. return 1000 / _frameDelay;
  112. }
  113. void Screen::setYOffset(int16 yOffset) {
  114. _yOffset = yOffset;
  115. }
  116. int16 Screen::getYOffset() {
  117. return _yOffset;
  118. }
  119. void Screen::setPaletteData(byte *paletteData) {
  120. _paletteChanged = true;
  121. _paletteData = paletteData;
  122. }
  123. void Screen::unsetPaletteData(byte *paletteData) {
  124. if (_paletteData == paletteData) {
  125. _paletteChanged = false;
  126. _paletteData = NULL;
  127. }
  128. }
  129. void Screen::testPalette(byte *paletteData) {
  130. if (_paletteData == paletteData)
  131. _paletteChanged = true;
  132. }
  133. void Screen::updatePalette() {
  134. if (_paletteChanged && _paletteData) {
  135. byte *tempPalette = new byte[768];
  136. for (int i = 0; i < 256; i++) {
  137. tempPalette[i * 3 + 0] = _paletteData[i * 4 + 0];
  138. tempPalette[i * 3 + 1] = _paletteData[i * 4 + 1];
  139. tempPalette[i * 3 + 2] = _paletteData[i * 4 + 2];
  140. }
  141. _vm->_system->getPaletteManager()->setPalette(tempPalette, 0, 256);
  142. delete[] tempPalette;
  143. _paletteChanged = false;
  144. }
  145. }
  146. void Screen::clear() {
  147. memset(_backScreen->getPixels(), 0, _backScreen->pitch * _backScreen->h);
  148. _fullRefresh = true;
  149. clearRenderQueue();
  150. }
  151. void Screen::clearRenderQueue() {
  152. _renderQueue->clear();
  153. _prevRenderQueue->clear();
  154. }
  155. void Screen::drawSurface2(const Graphics::Surface *surface, NDrawRect &drawRect, NRect &clipRect, bool transparent, byte version,
  156. const Graphics::Surface *shadowSurface) {
  157. int16 destX, destY;
  158. NRect ddRect;
  159. if (drawRect.x + drawRect.width >= clipRect.x2)
  160. ddRect.x2 = clipRect.x2 - drawRect.x;
  161. else
  162. ddRect.x2 = drawRect.width;
  163. if (drawRect.x < clipRect.x1) {
  164. destX = clipRect.x1;
  165. ddRect.x1 = clipRect.x1 - drawRect.x;
  166. } else {
  167. destX = drawRect.x;
  168. ddRect.x1 = 0;
  169. }
  170. if (drawRect.y + drawRect.height >= clipRect.y2)
  171. ddRect.y2 = clipRect.y2 - drawRect.y;
  172. else
  173. ddRect.y2 = drawRect.height;
  174. if (drawRect.y < clipRect.y1) {
  175. destY = clipRect.y1;
  176. ddRect.y1 = clipRect.y1 - drawRect.y;
  177. } else {
  178. destY = drawRect.y;
  179. ddRect.y1 = 0;
  180. }
  181. queueBlit(surface, destX, destY, ddRect, transparent, version, shadowSurface);
  182. }
  183. void Screen::drawSurface3(const Graphics::Surface *surface, int16 x, int16 y, NDrawRect &drawRect, NRect &clipRect, bool transparent, byte version) {
  184. int16 destX, destY;
  185. NRect ddRect;
  186. if (x + drawRect.width >= clipRect.x2)
  187. ddRect.x2 = clipRect.x2 - drawRect.x - x;
  188. else
  189. ddRect.x2 = drawRect.x + drawRect.width;
  190. if (x < clipRect.x1) {
  191. destX = clipRect.x1;
  192. ddRect.x1 = clipRect.x1 + drawRect.x - x;
  193. } else {
  194. destX = x;
  195. ddRect.x1 = drawRect.x;
  196. }
  197. if (y + drawRect.height >= clipRect.y2)
  198. ddRect.y2 = clipRect.y2 + drawRect.y - y;
  199. else
  200. ddRect.y2 = drawRect.y + drawRect.height;
  201. if (y < clipRect.y1) {
  202. destY = clipRect.y1;
  203. ddRect.y1 = clipRect.y1 + drawRect.y - y;
  204. } else {
  205. destY = y;
  206. ddRect.y1 = drawRect.y;
  207. }
  208. queueBlit(surface, destX, destY, ddRect, transparent, version);
  209. }
  210. void Screen::drawDoubleSurface2(const Graphics::Surface *surface, NDrawRect &drawRect) {
  211. const byte *source = (const byte*)surface->getPixels();
  212. byte *dest = (byte*)_backScreen->getBasePtr(drawRect.x, drawRect.y);
  213. for (int16 yc = 0; yc < surface->h; yc++) {
  214. byte *row = dest;
  215. for (int16 xc = 0; xc < surface->w; xc++) {
  216. *row++ = *source;
  217. *row++ = *source++;
  218. }
  219. memcpy(dest + _backScreen->pitch, dest, surface->w * 2);
  220. dest += _backScreen->pitch;
  221. dest += _backScreen->pitch;
  222. }
  223. _fullRefresh = true; // See Screen::update
  224. }
  225. void Screen::drawUnk(const Graphics::Surface *surface, NDrawRect &drawRect, NDrawRect &sysRect, NRect &clipRect, bool transparent, byte version) {
  226. int16 x, y;
  227. bool xflag, yflag;
  228. NDrawRect newDrawRect;
  229. x = sysRect.x;
  230. if (sysRect.width <= x || -sysRect.width >= x)
  231. x = x % sysRect.width;
  232. if (x < 0)
  233. x += sysRect.width;
  234. y = sysRect.y;
  235. if (y >= sysRect.height || -sysRect.height >= y)
  236. y = y % sysRect.height;
  237. if (y < 0)
  238. y += sysRect.height;
  239. xflag = x <= 0;
  240. yflag = y <= 0;
  241. newDrawRect.x = x;
  242. newDrawRect.width = sysRect.width - x;
  243. if (drawRect.width < newDrawRect.width) {
  244. xflag = true;
  245. newDrawRect.width = drawRect.width;
  246. }
  247. newDrawRect.y = y;
  248. newDrawRect.height = sysRect.height - y;
  249. if (drawRect.height < newDrawRect.height) {
  250. yflag = true;
  251. newDrawRect.height = drawRect.height;
  252. }
  253. drawSurface3(surface, drawRect.x, drawRect.y, newDrawRect, clipRect, transparent, version);
  254. if (!xflag) {
  255. newDrawRect.x = 0;
  256. newDrawRect.y = y;
  257. newDrawRect.width = x + drawRect.width - sysRect.width;
  258. newDrawRect.height = sysRect.height - y;
  259. if (drawRect.height < newDrawRect.height)
  260. newDrawRect.height = drawRect.height;
  261. drawSurface3(surface, sysRect.width + drawRect.x - x, drawRect.y, newDrawRect, clipRect, transparent, version);
  262. }
  263. if (!yflag) {
  264. newDrawRect.x = x;
  265. newDrawRect.y = 0;
  266. newDrawRect.width = sysRect.width - x;
  267. newDrawRect.height = y + drawRect.height - sysRect.height;
  268. if (drawRect.width < newDrawRect.width)
  269. newDrawRect.width = drawRect.width;
  270. drawSurface3(surface, drawRect.x, sysRect.height + drawRect.y - y, newDrawRect, clipRect, transparent, version);
  271. }
  272. if (!xflag && !yflag) {
  273. newDrawRect.x = 0;
  274. newDrawRect.y = 0;
  275. newDrawRect.width = x + drawRect.width - sysRect.width;
  276. newDrawRect.height = y + drawRect.height - sysRect.height;
  277. drawSurface3(surface, sysRect.width + drawRect.x - x, sysRect.height + drawRect.y - y, newDrawRect, clipRect, transparent, version);
  278. }
  279. }
  280. void Screen::drawSurfaceClipRects(const Graphics::Surface *surface, NDrawRect &drawRect, NRect *clipRects, uint clipRectsCount, bool transparent, byte version) {
  281. NDrawRect clipDrawRect(0, 0, drawRect.width, drawRect.height);
  282. for (uint i = 0; i < clipRectsCount; i++)
  283. drawSurface3(surface, drawRect.x, drawRect.y, clipDrawRect, clipRects[i], transparent, version);
  284. }
  285. void Screen::queueBlit(const Graphics::Surface *surface, int16 destX, int16 destY, NRect &ddRect, bool transparent, byte version,
  286. const Graphics::Surface *shadowSurface) {
  287. const int width = ddRect.x2 - ddRect.x1;
  288. const int height = ddRect.y2 - ddRect.y1;
  289. if (width <= 0 || height <= 0)
  290. return;
  291. RenderItem renderItem;
  292. renderItem._surface = surface;
  293. renderItem._shadowSurface = shadowSurface;
  294. renderItem._destX = destX;
  295. renderItem._destY = destY;
  296. renderItem._srcX = ddRect.x1;
  297. renderItem._srcY = ddRect.y1;
  298. renderItem._width = width;
  299. renderItem._height = height;
  300. renderItem._transparent = transparent;
  301. renderItem._version = version;
  302. _renderQueue->push_back(renderItem);
  303. }
  304. void Screen::blitRenderItem(const RenderItem &renderItem, const Common::Rect &clipRect) {
  305. const Graphics::Surface *surface = renderItem._surface;
  306. const Graphics::Surface *shadowSurface = renderItem._shadowSurface;
  307. const int16 x0 = MAX<int16>(clipRect.left, renderItem._destX);
  308. const int16 y0 = MAX<int16>(clipRect.top, renderItem._destY);
  309. const int16 x1 = MIN<int16>(clipRect.right, renderItem._destX + renderItem._width);
  310. const int16 y1 = MIN<int16>(clipRect.bottom, renderItem._destY + renderItem._height);
  311. const int16 width = x1 - x0;
  312. int16 height = y1 - y0;
  313. if (width < 0 || height < 0)
  314. return;
  315. const byte *source = (const byte*)surface->getBasePtr(renderItem._srcX + x0 - renderItem._destX, renderItem._srcY + y0 - renderItem._destY);
  316. byte *dest = (byte*)_backScreen->getBasePtr(x0, y0);
  317. if (shadowSurface) {
  318. const byte *shadowSource = (const byte*)shadowSurface->getBasePtr(x0, y0);
  319. while (height--) {
  320. for (int xc = 0; xc < width; xc++)
  321. if (source[xc] != 0)
  322. dest[xc] = shadowSource[xc];
  323. source += surface->pitch;
  324. shadowSource += shadowSurface->pitch;
  325. dest += _backScreen->pitch;
  326. }
  327. } else if (!renderItem._transparent) {
  328. while (height--) {
  329. memcpy(dest, source, width);
  330. source += surface->pitch;
  331. dest += _backScreen->pitch;
  332. }
  333. } else {
  334. while (height--) {
  335. for (int xc = 0; xc < width; xc++)
  336. if (source[xc] != 0)
  337. dest[xc] = source[xc];
  338. source += surface->pitch;
  339. dest += _backScreen->pitch;
  340. }
  341. }
  342. }
  343. } // End of namespace Neverhood