aboutsummaryrefslogtreecommitdiff
path: root/engines/toltecs/render.cpp
diff options
context:
space:
mode:
authorBenjamin Haisch2008-09-20 19:06:41 +0000
committerWillem Jan Palenstijn2011-11-20 22:43:06 +0100
commit7b97e8cd9370095b999b22371c1aef606a8464a1 (patch)
tree5b498423cef9e27432934da0b19794fbc1f2c038 /engines/toltecs/render.cpp
parent37a5f9e9ef9039808b225f598d02c1c48bc9fa95 (diff)
downloadscummvm-rg350-7b97e8cd9370095b999b22371c1aef606a8464a1.tar.gz
scummvm-rg350-7b97e8cd9370095b999b22371c1aef606a8464a1.tar.bz2
scummvm-rg350-7b97e8cd9370095b999b22371c1aef606a8464a1.zip
TOLTECS: A lot of changes in the graphics code:
- Optimized drawing code; now only items (sprites, text, screen masks) which have changed from the previous frame are redrawn, this speeds up things a lot - Implemented dirty rectangles using a microtile array - The previously committed Microtile Array implementation from SEL seemed buggy so I wrote my own version which works nicely so far (and is less code and GPL), only MicroTileArray::getRectangles uses parts from the old version, this will be changed later - One known bug related to dirty rectangles remains: Sometimes the background isn't restored correctly and gfx artifacts are visible
Diffstat (limited to 'engines/toltecs/render.cpp')
-rw-r--r--engines/toltecs/render.cpp320
1 files changed, 320 insertions, 0 deletions
diff --git a/engines/toltecs/render.cpp b/engines/toltecs/render.cpp
new file mode 100644
index 0000000000..dd7dfc212f
--- /dev/null
+++ b/engines/toltecs/render.cpp
@@ -0,0 +1,320 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ *
+ */
+
+#include "common/events.h"
+#include "common/keyboard.h"
+#include "common/file.h"
+#include "common/savefile.h"
+#include "common/config-manager.h"
+
+#include "base/plugins.h"
+#include "base/version.h"
+
+#include "toltecs/toltecs.h"
+#include "toltecs/render.h"
+#include "toltecs/resource.h"
+
+namespace Toltecs {
+
+Common::Rect makeRect(int16 x, int16 y, int16 width, int16 height) {
+ Common::Rect rect;
+ rect.left = x;
+ rect.top = y;
+ rect.setWidth(width);
+ rect.setHeight(height);
+ return rect;
+}
+
+RenderQueue::RenderQueue(ToltecsEngine *vm) : _vm(vm) {
+ _currQueue = new RenderQueueArray();
+ _prevQueue = new RenderQueueArray();
+ _updateUta = new MicroTileArray(640, 400);
+}
+
+RenderQueue::~RenderQueue() {
+ delete _currQueue;
+ delete _prevQueue;
+ delete _updateUta;
+}
+
+void RenderQueue::addSprite(SpriteDrawItem &sprite) {
+
+ RenderQueueItem item;
+ item.type = kSprite;
+ item.flags = kRefresh;
+ item.rect = makeRect(sprite.x - _vm->_cameraX, sprite.y - _vm->_cameraY, sprite.width, sprite.height);
+ item.priority = sprite.priority;
+
+ item.sprite = sprite;
+ item.sprite.x -= _vm->_cameraX;
+ item.sprite.y -= _vm->_cameraY;
+
+ // Add sprite sorted by priority
+ RenderQueueArray::iterator iter = _currQueue->begin();
+ while (iter != _currQueue->end() && (*iter).priority <= item.priority) {
+ iter++;
+ }
+ _currQueue->insert(iter, item);
+
+}
+
+void RenderQueue::addText(int16 x, int16 y, byte color, uint fontResIndex, byte *text, int len) {
+
+ Font font(_vm->_res->load(fontResIndex));
+
+ RenderQueueItem item;
+ item.type = kText;
+ item.flags = kRefresh;
+ item.rect = makeRect(x, y, font.getTextWidth(text), font.getHeight());
+ item.priority = 1000;
+
+ item.text.color = color;
+ item.text.fontResIndex = fontResIndex;
+ item.text.text = text;
+ item.text.len = len;
+
+ _currQueue->push_back(item);
+
+}
+
+void RenderQueue::addMask(SegmapMaskRect &mask) {
+
+ RenderQueueItem item;
+ item.type = kMask;
+ item.flags = kRefresh;
+ item.rect = makeRect(mask.x - _vm->_cameraX, mask.y - _vm->_cameraY, mask.width, mask.height);
+ item.priority = mask.priority;
+
+ item.mask = mask;
+
+ // Only add the mask if a sprite intersects its rect
+ if (rectIntersectsItem(item.rect)) {
+ RenderQueueArray::iterator iter = _currQueue->begin();
+ while (iter != _currQueue->end() && (*iter).priority <= item.priority) {
+ iter++;
+ }
+ _currQueue->insert(iter, item);
+ }
+
+}
+
+void RenderQueue::update() {
+
+ bool doFullRefresh = _vm->_screen->_fullRefresh;
+
+ _updateUta->clear();
+
+ if (!doFullRefresh) {
+
+ for (RenderQueueArray::iterator iter = _currQueue->begin(); iter != _currQueue->end(); iter++) {
+ RenderQueueItem *item = &(*iter);
+ RenderQueueItem *prevItem = findItemInQueue(_prevQueue, *item);
+ if (prevItem) {
+ if (hasItemChanged(*prevItem, *item)) {
+ item->flags = kRefresh;
+ addDirtyRect(prevItem->rect);
+ } else {
+ item->flags = kUnchanged;
+ }
+ } else {
+ item->flags = kRefresh;
+ }
+ }
+
+ for (RenderQueueArray::iterator iter = _prevQueue->begin(); iter != _prevQueue->end(); iter++) {
+ RenderQueueItem *prevItem = &(*iter);
+ RenderQueueItem *item = findItemInQueue(_currQueue, *prevItem);
+ if (!item) {
+ prevItem->flags = kRemoved;
+ addDirtyRect(prevItem->rect);
+ }
+ }
+
+ restoreDirtyBackground();
+
+ for (RenderQueueArray::iterator iter = _currQueue->begin(); iter != _currQueue->end(); iter++) {
+ RenderQueueItem *item = &(*iter);
+ if (item->flags != kUnchanged)
+ invalidateItemsByRect(item->rect, item);
+ }
+
+ } else {
+ byte *destp = _vm->_screen->_frontScreen;
+ byte *srcp = _vm->_screen->_backScreen + _vm->_cameraX + _vm->_cameraY * _vm->_sceneWidth;
+ int16 w = MIN<int16>(640, _vm->_sceneWidth);
+ int16 h = MIN<int16>(400, _vm->_cameraHeight);
+ while (h--) {
+ memcpy(destp, srcp, w);
+ destp += 640;
+ srcp += _vm->_sceneWidth;
+ }
+ _vm->_screen->_fullRefresh = false;
+ }
+
+ for (RenderQueueArray::iterator iter = _currQueue->begin(); iter != _currQueue->end(); iter++) {
+ const RenderQueueItem *item = &(*iter);
+
+ if (item->flags == kRefresh) {
+
+ switch (item->type) {
+ case kSprite:
+ _vm->_screen->drawSprite(item->sprite);
+ break;
+ case kText:
+ _vm->_screen->drawString(item->rect.left, item->rect.top, item->text.color, item->text.fontResIndex,
+ item->text.text, item->text.len, NULL, true);
+ break;
+ case kMask:
+ _vm->_screen->drawSurface(item->rect.left, item->rect.top, item->mask.surface);
+ break;
+ default:
+ break;
+ }
+
+ if (!doFullRefresh)
+ addDirtyRect(item->rect);
+
+ }
+
+ }
+
+ if (doFullRefresh) {
+ clear();
+ _vm->_system->copyRectToScreen((const byte *)_vm->_screen->_frontScreen, 640, 0, 0, 640, _vm->_cameraHeight);
+ } else {
+ updateDirtyRects();
+ }
+
+ SWAP(_currQueue, _prevQueue);
+ _currQueue->clear();
+
+}
+
+void RenderQueue::clear() {
+ _prevQueue->clear();
+ _currQueue->clear();
+}
+
+bool RenderQueue::rectIntersectsItem(const Common::Rect &rect) {
+ for (RenderQueueArray::iterator iter = _currQueue->begin(); iter != _currQueue->end(); iter++) {
+ const RenderQueueItem *item = &(*iter);
+ if (rect.intersects(item->rect))
+ return true;
+ }
+ return false;
+}
+
+RenderQueueItem *RenderQueue::findItemInQueue(RenderQueueArray *queue, const RenderQueueItem &item) {
+ /* This checks if the given item also exists in the previously drawn frame.
+ The state of the item (position, color etc) is handled elsewhere.
+ */
+ for (RenderQueueArray::iterator iter = queue->begin(); iter != queue->end(); iter++) {
+ RenderQueueItem *prevItem = &(*iter);
+ if (prevItem->type == item.type) {
+ switch (item.type) {
+ case kSprite:
+ if (prevItem->sprite.resIndex == item.sprite.resIndex &&
+ prevItem->sprite.frameNum == item.sprite.frameNum)
+ return prevItem;
+ break;
+ case kText:
+ if (prevItem->text.text == item.text.text &&
+ prevItem->text.len == item.text.len)
+ return prevItem;
+ break;
+ case kMask:
+ if (prevItem->mask.surface == item.mask.surface)
+ return prevItem;
+ break;
+ }
+ }
+ }
+ return NULL; // Not found
+}
+
+bool RenderQueue::hasItemChanged(const RenderQueueItem &item1, const RenderQueueItem &item2) {
+
+ // TODO: Clean up.
+
+ if (item1.type != item1.type)
+ return true;
+
+ if (item1.rect.left != item2.rect.left ||
+ item1.rect.top != item2.rect.top ||
+ item1.rect.right != item2.rect.right ||
+ item1.rect.bottom != item2.rect.bottom)
+ return true;
+
+ if (item1.type == kText && item1.text.color != item2.text.color)
+ return true;
+
+ return false;
+}
+
+void RenderQueue::invalidateItemsByRect(const Common::Rect &rect, const RenderQueueItem *item) {
+ for (RenderQueueArray::iterator iter = _currQueue->begin(); iter != _currQueue->end(); iter++) {
+ RenderQueueItem *subItem = &(*iter);
+ if (item != subItem &&
+ subItem->flags == kUnchanged &&
+ rect.intersects(subItem->rect)) {
+
+ subItem->flags = kRefresh;
+ invalidateItemsByRect(subItem->rect, subItem);
+ }
+ }
+}
+
+void RenderQueue::addDirtyRect(const Common::Rect &rect) {
+ _updateUta->addRect(rect);
+}
+
+void RenderQueue::restoreDirtyBackground() {
+ int n_rects = 0;
+ Common::Rect *rects = _updateUta->getRectangles(&n_rects, 0, 0, 639, _vm->_cameraHeight - 1);
+ for (int i = 0; i < n_rects; i++) {
+ byte *destp = _vm->_screen->_frontScreen + rects[i].left + rects[i].top * 640;
+ byte *srcp = _vm->_screen->_backScreen + (_vm->_cameraX + rects[i].left) + (_vm->_cameraY + rects[i].top) * _vm->_sceneWidth;
+ int16 w = rects[i].width();
+ int16 h = rects[i].height();
+ while (h--) {
+ memcpy(destp, srcp, w);
+ destp += 640;
+ srcp += _vm->_sceneWidth;
+ }
+ invalidateItemsByRect(rects[i], NULL);
+ }
+ delete[] rects;
+}
+
+void RenderQueue::updateDirtyRects() {
+ int n_rects = 0;
+ Common::Rect *rects = _updateUta->getRectangles(&n_rects, 0, 0, 639, _vm->_cameraHeight - 1);
+ for (int i = 0; i < n_rects; i++) {
+ _vm->_system->copyRectToScreen((const byte *)_vm->_screen->_frontScreen + rects[i].left + rects[i].top * 640,
+ 640, rects[i].left, rects[i].top, rects[i].width(), rects[i].height());
+ }
+ delete[] rects;
+}
+
+
+} // End of namespace Toltecs