aboutsummaryrefslogtreecommitdiff
path: root/engines
diff options
context:
space:
mode:
Diffstat (limited to 'engines')
-rw-r--r--engines/pegasus/graphics.cpp235
-rw-r--r--engines/pegasus/graphics.h66
2 files changed, 296 insertions, 5 deletions
diff --git a/engines/pegasus/graphics.cpp b/engines/pegasus/graphics.cpp
index 28aafdb04f..cec8595362 100644
--- a/engines/pegasus/graphics.cpp
+++ b/engines/pegasus/graphics.cpp
@@ -4,6 +4,9 @@
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
* 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
@@ -19,7 +22,7 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
-
+
#include "pegasus/graphics.h"
#include "common/endian.h"
@@ -28,17 +31,149 @@
#include "engines/util.h"
namespace Pegasus {
-
+
+DisplayElement::DisplayElement(const tDisplayElementID id) : IDObject(id) {
+ _elementIsDisplaying = false;
+ _elementIsVisible = false;
+ _elementOrder = 0;
+ _triggeredElement = this;
+ _nextElement = 0;
+}
+
+DisplayElement::~DisplayElement() {
+ if (isDisplaying())
+ ((PegasusEngine *)g_engine)->_gfx->removeDisplayElement(this);
+}
+
+void DisplayElement::setDisplayOrder(const tDisplayOrder order) {
+ if (_elementOrder != order) {
+ _elementOrder = order;
+ if (isDisplaying()) {
+ ((PegasusEngine *)g_engine)->_gfx->removeDisplayElement(this);
+ ((PegasusEngine *)g_engine)->_gfx->addDisplayElement(this);
+ triggerRedraw();
+ }
+ }
+}
+
+void DisplayElement::startDisplaying() {
+ if (!isDisplaying()) {
+ ((PegasusEngine *)g_engine)->_gfx->addDisplayElement(this);
+ triggerRedraw();
+ }
+}
+
+void DisplayElement::stopDisplaying() {
+ if (isDisplaying()) {
+ triggerRedraw();
+ ((PegasusEngine *)g_engine)->_gfx->removeDisplayElement(this);
+ }
+}
+
+void DisplayElement::setBounds(const tCoordType left, const tCoordType top, const tCoordType right, const tCoordType bottom) {
+ _bounds = Common::Rect(left, top, right, bottom);
+}
+
+void DisplayElement::getBounds(Common::Rect &r) const {
+ r = _bounds;
+}
+
+void DisplayElement::sizeElement(const tCoordType h, const tCoordType v) {
+ _bounds.right = _bounds.left + h;
+ _bounds.bottom = _bounds.top + v;
+}
+
+void DisplayElement::moveElementTo(const tCoordType h, const tCoordType v) {
+ _bounds.moveTo(h, v);
+}
+
+void DisplayElement::moveElement(const tCoordType dh, const tCoordType dv) {
+ _bounds.translate(dh, dv);
+}
+
+void DisplayElement::getLocation(tCoordType &h, tCoordType &v) const {
+ h = _bounds.left;
+ v = _bounds.top;
+}
+
+void DisplayElement::centerElementAt(const tCoordType h, const tCoordType v) {
+ _bounds.moveTo(h - (_bounds.width() / 2), v - (_bounds.height() / 2));
+}
+
+void DisplayElement::getCenter(tCoordType &h, tCoordType &v) const {
+ h = (_bounds.left + _bounds.right) / 2;
+ v = (_bounds.top + _bounds.bottom) / 2;
+}
+
+void DisplayElement::setBounds(const Common::Rect &r) {
+ if (r != _bounds) {
+ triggerRedraw();
+ _bounds = r;
+ triggerRedraw();
+ }
+}
+
+void DisplayElement::hide() {
+ if (_elementIsVisible) {
+ triggerRedraw();
+ _elementIsVisible = false;
+ }
+}
+
+void DisplayElement::show() {
+ if (!_elementIsVisible) {
+ _elementIsVisible = true;
+ triggerRedraw();
+ }
+}
+
+// Only invalidates this element's bounding rectangle if all these conditions are true:
+// -- The triggered element is this element.
+// -- The element is displaying on the display list.
+// -- The element is visible.
+// -- The element is part of the active layer OR is one of the reserved items.
+void DisplayElement::triggerRedraw() {
+ GraphicsManager *gfx = ((PegasusEngine *)g_engine)->_gfx;
+
+ if (_triggeredElement == this) {
+ if (validToDraw(gfx->getBackOfActiveLayer(), gfx->getFrontOfActiveLayer()))
+ gfx->invalRect(_bounds);
+ } else {
+ _triggeredElement->triggerRedraw();
+ }
+}
+
+void DisplayElement::setTriggeredElement(DisplayElement *element) {
+ if (element)
+ _triggeredElement = element;
+ else
+ _triggeredElement = this;
+}
+
+bool DisplayElement::validToDraw(tDisplayOrder backLayer, tDisplayOrder frontLayer) {
+ return isDisplaying() && _elementIsVisible &&
+ (getObjectID() <= kHighestReservedElementID ||
+ (getDisplayOrder() >= backLayer &&
+ getDisplayOrder() <= frontLayer));
+}
+
GraphicsManager::GraphicsManager(PegasusEngine *vm) : _vm(vm) {
initGraphics(640, 480, true, NULL);
-
+
+ // Old
_pictDecoder = new Graphics::PictDecoder(_vm->_system->getScreenFormat());
for (int i = 0; i < kImageCacheSize; i++)
_cache[i].surface = 0;
+
+ // New
+ _backLayer = kMinAvailableOrder;
+ _frontLayer = kMaxAvailableOrder;
+ _firstDisplayElement = _lastDisplayElement = 0;
}
GraphicsManager::~GraphicsManager() {
+ // Old
delete _pictDecoder;
for (int i = 0; i < kImageCacheSize; i++) {
@@ -168,5 +303,99 @@ int GraphicsManager::getImageSlot(const Common::String &filename) {
_cache[slot].lastUsed = _vm->_system->getMillis();
return slot;
}
+
+void GraphicsManager::invalRect(const Common::Rect &rect) {
+ // We're using a simpler algorithm for dirty rect handling than the original
+ // The original was way too overcomplicated for what we need here now.
+
+ if (_dirtyRect.width() == 0 || _dirtyRect.height() == 0) {
+ // We have no dirty rect, so this is now our dirty rect
+ _dirtyRect = rect;
+ } else {
+ // Expand our dirty rect to include rect
+ _dirtyRect.left = MIN<int>(_dirtyRect.left, rect.left);
+ _dirtyRect.top = MIN<int>(_dirtyRect.top, rect.top);
+ _dirtyRect.right = MAX<int>(_dirtyRect.right, rect.right);
+ _dirtyRect.bottom = MAX<int>(_dirtyRect.bottom, rect.bottom);
+ }
+}
+
+void GraphicsManager::addDisplayElement(DisplayElement *newElement) {
+ newElement->_elementOrder = CLIP<int>(newElement->_elementOrder, kMinAvailableOrder, kMaxAvailableOrder);
+
+ if (_firstDisplayElement) {
+ DisplayElement *runner = _firstDisplayElement;
+ DisplayElement *lastRunner = 0;
+
+ // Search for first element whose display order is greater than
+ // the new element's and add the new element just before it.
+ while (runner) {
+ if (newElement->_elementOrder < runner->_elementOrder) {
+ if (lastRunner) {
+ lastRunner->_nextElement = newElement;
+ newElement->_nextElement = runner;
+ } else {
+ newElement->_nextElement = _firstDisplayElement;
+ _firstDisplayElement = newElement;
+ }
+ break;
+ }
+ lastRunner = runner;
+ runner = runner->_nextElement;
+ }
+
+ // If got here and runner == NULL, we ran through the whole list without
+ // inserting, so add at the end.
+ if (!runner) {
+ _lastDisplayElement->_nextElement = newElement;
+ _lastDisplayElement = newElement;
+ }
+ } else {
+ _firstDisplayElement = newElement;
+ _lastDisplayElement = newElement;
+ }
+
+ newElement->_elementIsDisplaying = true;
+}
+
+void GraphicsManager::removeDisplayElement(DisplayElement *oldElement) {
+ if (!_firstDisplayElement)
+ return;
+
+ if (oldElement == _firstDisplayElement) {
+ if (oldElement == _lastDisplayElement) {
+ _firstDisplayElement = 0;
+ _lastDisplayElement = 0;
+ } else {
+ _firstDisplayElement = oldElement->_nextElement;
+ }
+
+ invalRect(oldElement->_bounds);
+ } else {
+ // Scan list for element.
+ // If we get here, we know that the list has at least one item, and it
+ // is not the first item, so we can skip it.
+ DisplayElement *runner = _firstDisplayElement->_nextElement;
+ DisplayElement *lastRunner = _firstDisplayElement;
+
+ while (runner) {
+ if (runner == oldElement) {
+ lastRunner->_nextElement = runner->_nextElement;
+
+ if (oldElement == _lastDisplayElement)
+ _lastDisplayElement = lastRunner;
+
+ invalRect(oldElement->_bounds);
+ break;
+ }
+
+ lastRunner = runner;
+ runner = runner->_nextElement;
+ }
+ }
+
+ oldElement->_nextElement = 0;
+ oldElement->_elementIsDisplaying = false;
+}
} // End of namespace Pegasus
diff --git a/engines/pegasus/graphics.h b/engines/pegasus/graphics.h
index 41353735b1..6778adb323 100644
--- a/engines/pegasus/graphics.h
+++ b/engines/pegasus/graphics.h
@@ -4,6 +4,9 @@
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
* 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
@@ -30,9 +33,55 @@
#include "graphics/surface.h"
#include "pegasus/pegasus.h"
+#include "pegasus/util.h"
namespace Pegasus {
+class DisplayElement : public IDObject {
+friend class GraphicsManager;
+public:
+ DisplayElement(const tDisplayElementID);
+ virtual ~DisplayElement();
+
+ void setDisplayOrder(const tDisplayOrder);
+ tDisplayOrder getDisplayOrder() const { return _elementOrder; }
+
+ bool validToDraw(tDisplayOrder, tDisplayOrder);
+
+ virtual void draw(const Common::Rect&) {}
+ bool isDisplaying() { return _elementIsDisplaying; }
+ virtual void startDisplaying();
+ virtual void stopDisplaying();
+
+ virtual void show();
+ virtual void hide();
+ bool isVisible() { return _elementIsVisible; }
+
+ // triggerRedraw only triggers a draw if the element is displaying and visible.
+ void triggerRedraw();
+ void setTriggeredElement(DisplayElement *);
+
+ virtual void setBounds(const tCoordType, const tCoordType, const tCoordType, const tCoordType);
+ virtual void setBounds(const Common::Rect&);
+ virtual void getBounds(Common::Rect&) const;
+ virtual void sizeElement(const tCoordType, const tCoordType);
+ virtual void moveElementTo(const tCoordType, const tCoordType);
+ virtual void moveElement(const tCoordType, const tCoordType);
+ virtual void getLocation(tCoordType&, tCoordType&) const;
+ virtual void getCenter(tCoordType&, tCoordType&) const;
+ virtual void centerElementAt(const tCoordType, const tCoordType);
+
+protected:
+ Common::Rect _bounds;
+ bool _elementIsVisible;
+ DisplayElement *_triggeredElement;
+
+ // Used only by PegasusEngine
+ bool _elementIsDisplaying;
+ tDisplayOrder _elementOrder;
+ DisplayElement *_nextElement;
+};
+
enum {
kImageCacheSize = 10
};
@@ -50,17 +99,30 @@ public:
GraphicsManager(PegasusEngine *vm);
~GraphicsManager();
+ // Older "temporary" API
void drawPict(Common::String filename, int x, int y, bool updateScreen = true);
void drawPictTransparent(Common::String filename, int x, int y, uint32 transparency, bool updateScreen = true);
uint32 getColor(byte r, byte g, byte b);
-
+
+ // Newer "to-be-used" API
+ void addDisplayElement(DisplayElement *element);
+ void removeDisplayElement(DisplayElement *element);
+ void invalRect(const Common::Rect &rect);
+ tDisplayOrder getBackOfActiveLayer() const { return _backLayer; }
+ tDisplayOrder getFrontOfActiveLayer() const { return _frontLayer; }
private:
PegasusEngine *_vm;
- Graphics::PictDecoder *_pictDecoder;
+ // Older "temporary" API
+ Graphics::PictDecoder *_pictDecoder;
Graphics::Surface *decodeImage(const Common::String &filename);
ImageCache _cache[kImageCacheSize];
int getImageSlot(const Common::String &filename);
+
+ // Newer "to-be-used" API
+ Common::Rect _dirtyRect;
+ tDisplayOrder _backLayer, _frontLayer;
+ DisplayElement *_firstDisplayElement, *_lastDisplayElement;
};
} // End of namespace Pegasus