aboutsummaryrefslogtreecommitdiff
path: root/engines/sword25/gfx
diff options
context:
space:
mode:
Diffstat (limited to 'engines/sword25/gfx')
-rw-r--r--engines/sword25/gfx/animation.cpp680
-rw-r--r--engines/sword25/gfx/animation.h219
-rw-r--r--engines/sword25/gfx/animationdescription.cpp65
-rw-r--r--engines/sword25/gfx/animationdescription.h103
-rw-r--r--engines/sword25/gfx/animationresource.cpp252
-rw-r--r--engines/sword25/gfx/animationresource.h123
-rw-r--r--engines/sword25/gfx/animationtemplate.cpp243
-rw-r--r--engines/sword25/gfx/animationtemplate.h125
-rw-r--r--engines/sword25/gfx/animationtemplateregistry.cpp107
-rw-r--r--engines/sword25/gfx/animationtemplateregistry.h64
-rw-r--r--engines/sword25/gfx/bitmap.cpp183
-rw-r--r--engines/sword25/gfx/bitmap.h189
-rw-r--r--engines/sword25/gfx/bitmapresource.cpp62
-rw-r--r--engines/sword25/gfx/bitmapresource.h212
-rw-r--r--engines/sword25/gfx/dynamicbitmap.cpp155
-rw-r--r--engines/sword25/gfx/dynamicbitmap.h78
-rw-r--r--engines/sword25/gfx/fontresource.cpp138
-rw-r--r--engines/sword25/gfx/fontresource.h134
-rw-r--r--engines/sword25/gfx/framecounter.cpp68
-rw-r--r--engines/sword25/gfx/framecounter.h99
-rw-r--r--engines/sword25/gfx/graphicengine.cpp494
-rw-r--r--engines/sword25/gfx/graphicengine.h392
-rw-r--r--engines/sword25/gfx/graphicengine_script.cpp1305
-rw-r--r--engines/sword25/gfx/image/art.cpp2653
-rw-r--r--engines/sword25/gfx/image/art.h279
-rw-r--r--engines/sword25/gfx/image/image.h222
-rw-r--r--engines/sword25/gfx/image/pngloader.cpp255
-rw-r--r--engines/sword25/gfx/image/pngloader.h93
-rw-r--r--engines/sword25/gfx/image/renderedimage.cpp397
-rw-r--r--engines/sword25/gfx/image/renderedimage.h121
-rw-r--r--engines/sword25/gfx/image/swimage.cpp115
-rw-r--r--engines/sword25/gfx/image/swimage.h99
-rw-r--r--engines/sword25/gfx/image/vectorimage.cpp636
-rw-r--r--engines/sword25/gfx/image/vectorimage.h237
-rw-r--r--engines/sword25/gfx/image/vectorimagerenderer.cpp461
-rw-r--r--engines/sword25/gfx/panel.cpp111
-rw-r--r--engines/sword25/gfx/panel.h73
-rw-r--r--engines/sword25/gfx/renderobject.cpp517
-rw-r--r--engines/sword25/gfx/renderobject.h525
-rw-r--r--engines/sword25/gfx/renderobjectmanager.cpp151
-rw-r--r--engines/sword25/gfx/renderobjectmanager.h129
-rw-r--r--engines/sword25/gfx/renderobjectptr.h79
-rw-r--r--engines/sword25/gfx/renderobjectregistry.cpp51
-rw-r--r--engines/sword25/gfx/renderobjectregistry.h57
-rw-r--r--engines/sword25/gfx/rootrenderobject.h72
-rw-r--r--engines/sword25/gfx/screenshot.cpp185
-rw-r--r--engines/sword25/gfx/screenshot.h51
-rw-r--r--engines/sword25/gfx/staticbitmap.cpp185
-rw-r--r--engines/sword25/gfx/staticbitmap.h81
-rw-r--r--engines/sword25/gfx/text.cpp358
-rw-r--r--engines/sword25/gfx/text.h169
-rw-r--r--engines/sword25/gfx/timedrenderobject.cpp52
-rw-r--r--engines/sword25/gfx/timedrenderobject.h59
53 files changed, 13963 insertions, 0 deletions
diff --git a/engines/sword25/gfx/animation.cpp b/engines/sword25/gfx/animation.cpp
new file mode 100644
index 0000000000..0d3baae347
--- /dev/null
+++ b/engines/sword25/gfx/animation.cpp
@@ -0,0 +1,680 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#include "sword25/gfx/animation.h"
+
+#include "sword25/kernel/kernel.h"
+#include "sword25/kernel/resmanager.h"
+#include "sword25/kernel/inputpersistenceblock.h"
+#include "sword25/kernel/outputpersistenceblock.h"
+#include "sword25/package/packagemanager.h"
+#include "sword25/gfx/image/image.h"
+#include "sword25/gfx/animationtemplate.h"
+#include "sword25/gfx/animationtemplateregistry.h"
+#include "sword25/gfx/animationresource.h"
+#include "sword25/gfx/bitmapresource.h"
+#include "sword25/gfx/graphicengine.h"
+
+namespace Sword25 {
+
+#define BS_LOG_PREFIX "ANIMATION"
+
+Animation::Animation(RenderObjectPtr<RenderObject> parentPtr, const Common::String &fileName) :
+ TimedRenderObject(parentPtr, RenderObject::TYPE_ANIMATION) {
+ // Das BS_RenderObject konnte nicht erzeugt werden, daher muss an dieser Stelle abgebrochen werden.
+ if (!_initSuccess)
+ return;
+
+ initMembers();
+
+ // Vom negativen Fall ausgehen.
+ _initSuccess = false;
+
+ initializeAnimationResource(fileName);
+
+ // Erfolg signalisieren.
+ _initSuccess = true;
+}
+
+Animation::Animation(RenderObjectPtr<RenderObject> parentPtr, const AnimationTemplate &templ) :
+ TimedRenderObject(parentPtr, RenderObject::TYPE_ANIMATION) {
+ // Das BS_RenderObject konnte nicht erzeugt werden, daher muss an dieser Stelle abgebrochen werden.
+ if (!_initSuccess)
+ return;
+
+ initMembers();
+
+ // Vom negativen Fall ausgehen.
+ _initSuccess = false;
+
+ _animationTemplateHandle = AnimationTemplate::create(templ);
+
+ // Erfolg signalisieren.
+ _initSuccess = true;
+}
+
+Animation::Animation(InputPersistenceBlock &reader, RenderObjectPtr<RenderObject> parentPtr, uint handle) :
+ TimedRenderObject(parentPtr, RenderObject::TYPE_ANIMATION, handle) {
+ // Das BS_RenderObject konnte nicht erzeugt werden, daher muss an dieser Stelle abgebrochen werden.
+ if (!_initSuccess)
+ return;
+
+ initMembers();
+
+ // Objekt vom Stream laden.
+ _initSuccess = unpersist(reader);
+}
+
+void Animation::initializeAnimationResource(const Common::String &fileName) {
+ // Die Resource wird für die gesamte Lebensdauer des Animations-Objektes gelockt.
+ Resource *resourcePtr = Kernel::getInstance()->getResourceManager()->requestResource(fileName);
+ if (resourcePtr && resourcePtr->getType() == Resource::TYPE_ANIMATION)
+ _animationResourcePtr = static_cast<AnimationResource *>(resourcePtr);
+ else {
+ BS_LOG_ERRORLN("The resource \"%s\" could not be requested. The Animation can't be created.", fileName.c_str());
+ return;
+ }
+
+ // Größe und Position der Animation anhand des aktuellen Frames bestimmen.
+ computeCurrentCharacteristics();
+}
+
+void Animation::initMembers() {
+ _currentFrame = 0;
+ _currentFrameTime = 0;
+ _direction = FORWARD;
+ _running = false;
+ _finished = false;
+ _relX = 0;
+ _relY = 0;
+ _scaleFactorX = 1.0f;
+ _scaleFactorY = 1.0f;
+ _modulationColor = 0xffffffff;
+ _animationResourcePtr = 0;
+ _animationTemplateHandle = 0;
+ _framesLocked = false;
+}
+
+Animation::~Animation() {
+ if (getAnimationDescription()) {
+ stop();
+ getAnimationDescription()->unlock();
+ }
+
+ // Invoke the "delete" callback
+ if (_deleteCallback)
+ (_deleteCallback)(getHandle());
+
+}
+
+void Animation::play() {
+ // If the animation was completed, then play it again from the start.
+ if (_finished)
+ stop();
+
+ _running = true;
+ lockAllFrames();
+}
+
+void Animation::pause() {
+ _running = false;
+ unlockAllFrames();
+}
+
+void Animation::stop() {
+ _currentFrame = 0;
+ _currentFrameTime = 0;
+ _direction = FORWARD;
+ pause();
+}
+
+void Animation::setFrame(uint nr) {
+ AnimationDescription *animationDescriptionPtr = getAnimationDescription();
+ BS_ASSERT(animationDescriptionPtr);
+
+ if (nr >= animationDescriptionPtr->getFrameCount()) {
+ BS_LOG_ERRORLN("Tried to set animation to illegal frame (%d). Value must be between 0 and %d.",
+ nr, animationDescriptionPtr->getFrameCount());
+ return;
+ }
+
+ _currentFrame = nr;
+ _currentFrameTime = 0;
+ computeCurrentCharacteristics();
+ forceRefresh();
+}
+
+bool Animation::doRender() {
+ AnimationDescription *animationDescriptionPtr = getAnimationDescription();
+ BS_ASSERT(animationDescriptionPtr);
+ BS_ASSERT(_currentFrame < animationDescriptionPtr->getFrameCount());
+
+ // Bitmap des aktuellen Frames holen
+ Resource *pResource = Kernel::getInstance()->getResourceManager()->requestResource(animationDescriptionPtr->getFrame(_currentFrame).fileName);
+ BS_ASSERT(pResource);
+ BS_ASSERT(pResource->getType() == Resource::TYPE_BITMAP);
+ BitmapResource *pBitmapResource = static_cast<BitmapResource *>(pResource);
+
+ // Framebufferobjekt holen
+ GraphicEngine *pGfx = Kernel::getInstance()->getGfx();
+ BS_ASSERT(pGfx);
+
+ // Bitmap zeichnen
+ bool result;
+ if (isScalingAllowed() && (_width != pBitmapResource->getWidth() || _height != pBitmapResource->getHeight())) {
+ result = pBitmapResource->blit(_absoluteX, _absoluteY,
+ (animationDescriptionPtr->getFrame(_currentFrame).flipV ? BitmapResource::FLIP_V : 0) |
+ (animationDescriptionPtr->getFrame(_currentFrame).flipH ? BitmapResource::FLIP_H : 0),
+ 0, _modulationColor, _width, _height);
+ } else {
+ result = pBitmapResource->blit(_absoluteX, _absoluteY,
+ (animationDescriptionPtr->getFrame(_currentFrame).flipV ? BitmapResource::FLIP_V : 0) |
+ (animationDescriptionPtr->getFrame(_currentFrame).flipH ? BitmapResource::FLIP_H : 0),
+ 0, _modulationColor, -1, -1);
+ }
+
+ // Resource freigeben
+ pBitmapResource->release();
+
+ return result;
+}
+
+void Animation::frameNotification(int timeElapsed) {
+ AnimationDescription *animationDescriptionPtr = getAnimationDescription();
+ BS_ASSERT(animationDescriptionPtr);
+ BS_ASSERT(timeElapsed >= 0);
+
+ // Nur wenn die Animation läuft wird sie auch weiterbewegt
+ if (_running) {
+ // Gesamte vergangene Zeit bestimmen (inkl. Restzeit des aktuellen Frames)
+ _currentFrameTime += timeElapsed;
+
+ // Anzahl an zu überpringenden Frames bestimmen
+ int skipFrames = animationDescriptionPtr->getMillisPerFrame() == 0 ? 0 : _currentFrameTime / animationDescriptionPtr->getMillisPerFrame();
+
+ // Neue Frame-Restzeit bestimmen
+ _currentFrameTime -= animationDescriptionPtr->getMillisPerFrame() * skipFrames;
+
+ // Neuen Frame bestimmen (je nach aktuellener Abspielrichtung wird addiert oder subtrahiert)
+ int tmpCurFrame = _currentFrame;
+ switch (_direction) {
+ case FORWARD:
+ tmpCurFrame += skipFrames;
+ break;
+
+ case BACKWARD:
+ tmpCurFrame -= skipFrames;
+ break;
+
+ default:
+ BS_ASSERT(0);
+ }
+
+ // Deal with overflows
+ if (tmpCurFrame < 0) {
+ // Loop-Point callback
+ if (_loopPointCallback && !(_loopPointCallback)(getHandle()))
+ _loopPointCallback = 0;
+
+ // An underflow may only occur if the animation type is JOJO.
+ BS_ASSERT(animationDescriptionPtr->getAnimationType() == AT_JOJO);
+ tmpCurFrame = - tmpCurFrame;
+ _direction = FORWARD;
+ } else if (static_cast<uint>(tmpCurFrame) >= animationDescriptionPtr->getFrameCount()) {
+ // Loop-Point callback
+ if (_loopPointCallback && !(_loopPointCallback)(getHandle()))
+ _loopPointCallback = 0;
+
+ switch (animationDescriptionPtr->getAnimationType()) {
+ case AT_ONESHOT:
+ tmpCurFrame = animationDescriptionPtr->getFrameCount() - 1;
+ _finished = true;
+ pause();
+ break;
+
+ case AT_LOOP:
+ tmpCurFrame = tmpCurFrame % animationDescriptionPtr->getFrameCount();
+ break;
+
+ case AT_JOJO:
+ tmpCurFrame = animationDescriptionPtr->getFrameCount() - (tmpCurFrame % animationDescriptionPtr->getFrameCount()) - 1;
+ _direction = BACKWARD;
+ break;
+
+ default:
+ BS_ASSERT(0);
+ }
+ }
+
+ if ((int)_currentFrame != tmpCurFrame) {
+ forceRefresh();
+
+ if (animationDescriptionPtr->getFrame(_currentFrame).action != "") {
+ // action callback
+ if (_actionCallback && !(_actionCallback)(getHandle()))
+ _actionCallback = 0;
+ }
+ }
+
+ _currentFrame = static_cast<uint>(tmpCurFrame);
+ }
+
+ // Größe und Position der Animation anhand des aktuellen Frames bestimmen
+ computeCurrentCharacteristics();
+
+ BS_ASSERT(_currentFrame < animationDescriptionPtr->getFrameCount());
+ BS_ASSERT(_currentFrameTime >= 0);
+}
+
+void Animation::computeCurrentCharacteristics() {
+ AnimationDescription *animationDescriptionPtr = getAnimationDescription();
+ BS_ASSERT(animationDescriptionPtr);
+ const AnimationResource::Frame &curFrame = animationDescriptionPtr->getFrame(_currentFrame);
+
+ Resource *pResource = Kernel::getInstance()->getResourceManager()->requestResource(curFrame.fileName);
+ BS_ASSERT(pResource);
+ BS_ASSERT(pResource->getType() == Resource::TYPE_BITMAP);
+ BitmapResource *pBitmap = static_cast<BitmapResource *>(pResource);
+
+ // Größe des Bitmaps auf die Animation übertragen
+ _width = static_cast<int>(pBitmap->getWidth() * _scaleFactorX);
+ _height = static_cast<int>(pBitmap->getHeight() * _scaleFactorY);
+
+ // Position anhand des Hotspots berechnen und setzen
+ int posX = _relX + computeXModifier();
+ int posY = _relY + computeYModifier();
+
+ RenderObject::setPos(posX, posY);
+
+ pBitmap->release();
+}
+
+bool Animation::lockAllFrames() {
+ if (!_framesLocked) {
+ AnimationDescription *animationDescriptionPtr = getAnimationDescription();
+ BS_ASSERT(animationDescriptionPtr);
+ for (uint i = 0; i < animationDescriptionPtr->getFrameCount(); ++i) {
+ if (!Kernel::getInstance()->getResourceManager()->requestResource(animationDescriptionPtr->getFrame(i).fileName)) {
+ BS_LOG_ERRORLN("Could not lock all animation frames.");
+ return false;
+ }
+ }
+
+ _framesLocked = true;
+ }
+
+ return true;
+}
+
+bool Animation::unlockAllFrames() {
+ if (_framesLocked) {
+ AnimationDescription *animationDescriptionPtr = getAnimationDescription();
+ BS_ASSERT(animationDescriptionPtr);
+ for (uint i = 0; i < animationDescriptionPtr->getFrameCount(); ++i) {
+ Resource *pResource;
+ if (!(pResource = Kernel::getInstance()->getResourceManager()->requestResource(animationDescriptionPtr->getFrame(i).fileName))) {
+ BS_LOG_ERRORLN("Could not unlock all animation frames.");
+ return false;
+ }
+
+ // Zwei mal freigeben um den Request von LockAllFrames() und den jetzigen Request aufzuheben
+ pResource->release();
+ if (pResource->getLockCount())
+ pResource->release();
+ }
+
+ _framesLocked = false;
+ }
+
+ return true;
+}
+
+Animation::ANIMATION_TYPES Animation::getAnimationType() const {
+ AnimationDescription *animationDescriptionPtr = getAnimationDescription();
+ BS_ASSERT(animationDescriptionPtr);
+ return animationDescriptionPtr->getAnimationType();
+}
+
+int Animation::getFPS() const {
+ AnimationDescription *animationDescriptionPtr = getAnimationDescription();
+ BS_ASSERT(animationDescriptionPtr);
+ return animationDescriptionPtr->getFPS();
+}
+
+int Animation::getFrameCount() const {
+ AnimationDescription *animationDescriptionPtr = getAnimationDescription();
+ BS_ASSERT(animationDescriptionPtr);
+ return animationDescriptionPtr->getFrameCount();
+}
+
+bool Animation::isScalingAllowed() const {
+ AnimationDescription *animationDescriptionPtr = getAnimationDescription();
+ BS_ASSERT(animationDescriptionPtr);
+ return animationDescriptionPtr->isScalingAllowed();
+}
+
+bool Animation::isAlphaAllowed() const {
+ AnimationDescription *animationDescriptionPtr = getAnimationDescription();
+ BS_ASSERT(animationDescriptionPtr);
+ return animationDescriptionPtr->isAlphaAllowed();
+}
+
+bool Animation::isColorModulationAllowed() const {
+ AnimationDescription *animationDescriptionPtr = getAnimationDescription();
+ BS_ASSERT(animationDescriptionPtr);
+ return animationDescriptionPtr->isColorModulationAllowed();
+}
+
+void Animation::setPos(int relX, int relY) {
+ _relX = relX;
+ _relY = relY;
+
+ computeCurrentCharacteristics();
+}
+
+void Animation::setX(int relX) {
+ _relX = relX;
+
+ computeCurrentCharacteristics();
+}
+
+void Animation::setY(int relY) {
+ _relY = relY;
+
+ computeCurrentCharacteristics();
+}
+
+void Animation::setAlpha(int alpha) {
+ AnimationDescription *animationDescriptionPtr = getAnimationDescription();
+ BS_ASSERT(animationDescriptionPtr);
+ if (!animationDescriptionPtr->isAlphaAllowed()) {
+ BS_LOG_WARNINGLN("Tried to set alpha value on an animation that does not support alpha. Call was ignored.");
+ return;
+ }
+
+ uint newModulationColor = (_modulationColor & 0x00ffffff) | alpha << 24;
+ if (newModulationColor != _modulationColor) {
+ _modulationColor = newModulationColor;
+ forceRefresh();
+ }
+}
+
+void Animation::setModulationColor(uint modulationColor) {
+ AnimationDescription *animationDescriptionPtr = getAnimationDescription();
+ BS_ASSERT(animationDescriptionPtr);
+ if (!animationDescriptionPtr->isColorModulationAllowed()) {
+ BS_LOG_WARNINGLN("Tried to set modulation color on an animation that does not support color modulation. Call was ignored");
+ return;
+ }
+
+ uint newModulationColor = (modulationColor & 0x00ffffff) | (_modulationColor & 0xff000000);
+ if (newModulationColor != _modulationColor) {
+ _modulationColor = newModulationColor;
+ forceRefresh();
+ }
+}
+
+void Animation::setScaleFactor(float scaleFactor) {
+ setScaleFactorX(scaleFactor);
+ setScaleFactorY(scaleFactor);
+}
+
+void Animation::setScaleFactorX(float scaleFactorX) {
+ AnimationDescription *animationDescriptionPtr = getAnimationDescription();
+ BS_ASSERT(animationDescriptionPtr);
+ if (!animationDescriptionPtr->isScalingAllowed()) {
+ BS_LOG_WARNINGLN("Tried to set x scale factor on an animation that does not support scaling. Call was ignored");
+ return;
+ }
+
+ if (scaleFactorX != _scaleFactorX) {
+ _scaleFactorX = scaleFactorX;
+ if (_scaleFactorX <= 0.0f)
+ _scaleFactorX = 0.001f;
+ forceRefresh();
+ computeCurrentCharacteristics();
+ }
+}
+
+void Animation::setScaleFactorY(float scaleFactorY) {
+ AnimationDescription *animationDescriptionPtr = getAnimationDescription();
+ BS_ASSERT(animationDescriptionPtr);
+ if (!animationDescriptionPtr->isScalingAllowed()) {
+ BS_LOG_WARNINGLN("Tried to set y scale factor on an animation that does not support scaling. Call was ignored");
+ return;
+ }
+
+ if (scaleFactorY != _scaleFactorY) {
+ _scaleFactorY = scaleFactorY;
+ if (_scaleFactorY <= 0.0f)
+ _scaleFactorY = 0.001f;
+ forceRefresh();
+ computeCurrentCharacteristics();
+ }
+}
+
+const Common::String &Animation::getCurrentAction() const {
+ AnimationDescription *animationDescriptionPtr = getAnimationDescription();
+ BS_ASSERT(animationDescriptionPtr);
+ return animationDescriptionPtr->getFrame(_currentFrame).action;
+}
+
+int Animation::getX() const {
+ return _relX;
+}
+
+int Animation::getY() const {
+ return _relY;
+}
+
+int Animation::getAbsoluteX() const {
+ return _absoluteX + (_relX - _x);
+}
+
+int Animation::getAbsoluteY() const {
+ return _absoluteY + (_relY - _y);
+}
+
+int Animation::computeXModifier() const {
+ AnimationDescription *animationDescriptionPtr = getAnimationDescription();
+ BS_ASSERT(animationDescriptionPtr);
+ const AnimationResource::Frame &curFrame = animationDescriptionPtr->getFrame(_currentFrame);
+
+ Resource *pResource = Kernel::getInstance()->getResourceManager()->requestResource(curFrame.fileName);
+ BS_ASSERT(pResource);
+ BS_ASSERT(pResource->getType() == Resource::TYPE_BITMAP);
+ BitmapResource *pBitmap = static_cast<BitmapResource *>(pResource);
+
+ int result = curFrame.flipV ? - static_cast<int>((pBitmap->getWidth() - 1 - curFrame.hotspotX) * _scaleFactorX) :
+ - static_cast<int>(curFrame.hotspotX * _scaleFactorX);
+
+ pBitmap->release();
+
+ return result;
+}
+
+int Animation::computeYModifier() const {
+ AnimationDescription *animationDescriptionPtr = getAnimationDescription();
+ BS_ASSERT(animationDescriptionPtr);
+ const AnimationResource::Frame &curFrame = animationDescriptionPtr->getFrame(_currentFrame);
+
+ Resource *pResource = Kernel::getInstance()->getResourceManager()->requestResource(curFrame.fileName);
+ BS_ASSERT(pResource);
+ BS_ASSERT(pResource->getType() == Resource::TYPE_BITMAP);
+ BitmapResource *pBitmap = static_cast<BitmapResource *>(pResource);
+
+ int result = curFrame.flipH ? - static_cast<int>((pBitmap->getHeight() - 1 - curFrame.hotspotY) * _scaleFactorY) :
+ - static_cast<int>(curFrame.hotspotY * _scaleFactorY);
+
+ pBitmap->release();
+
+ return result;
+}
+
+bool Animation::persist(OutputPersistenceBlock &writer) {
+ bool result = true;
+
+ result &= RenderObject::persist(writer);
+
+ writer.write(_relX);
+ writer.write(_relY);
+ writer.write(_scaleFactorX);
+ writer.write(_scaleFactorY);
+ writer.write(_modulationColor);
+ writer.write(_currentFrame);
+ writer.write(_currentFrameTime);
+ writer.write(_running);
+ writer.write(_finished);
+ writer.write(static_cast<uint>(_direction));
+
+ // Je nach Animationstyp entweder das Template oder die Ressource speichern.
+ if (_animationResourcePtr) {
+ uint marker = 0;
+ writer.write(marker);
+ writer.writeString(_animationResourcePtr->getFileName());
+ } else if (_animationTemplateHandle) {
+ uint marker = 1;
+ writer.write(marker);
+ writer.write(_animationTemplateHandle);
+ } else {
+ BS_ASSERT(false);
+ }
+
+ //writer.write(_AnimationDescriptionPtr);
+
+ writer.write(_framesLocked);
+
+ // The following is only there to for compatibility with older saves
+ // resp. the original engine.
+ writer.write((uint)1);
+ writer.writeString("LuaLoopPointCB");
+ writer.write(getHandle());
+ writer.write((uint)1);
+ writer.writeString("LuaActionCB");
+ writer.write(getHandle());
+ writer.write((uint)1);
+ writer.writeString("LuaDeleteCB");
+ writer.write(getHandle());
+
+ result &= RenderObject::persistChildren(writer);
+
+ return result;
+}
+
+// -----------------------------------------------------------------------------
+
+bool Animation::unpersist(InputPersistenceBlock &reader) {
+ bool result = true;
+
+ result &= RenderObject::unpersist(reader);
+
+ reader.read(_relX);
+ reader.read(_relY);
+ reader.read(_scaleFactorX);
+ reader.read(_scaleFactorY);
+ reader.read(_modulationColor);
+ reader.read(_currentFrame);
+ reader.read(_currentFrameTime);
+ reader.read(_running);
+ reader.read(_finished);
+ uint direction;
+ reader.read(direction);
+ _direction = static_cast<Direction>(direction);
+
+ // Animationstyp einlesen.
+ uint marker;
+ reader.read(marker);
+ if (marker == 0) {
+ Common::String resourceFilename;
+ reader.readString(resourceFilename);
+ initializeAnimationResource(resourceFilename);
+ } else if (marker == 1) {
+ reader.read(_animationTemplateHandle);
+ } else {
+ BS_ASSERT(false);
+ }
+
+ reader.read(_framesLocked);
+ if (_framesLocked)
+ lockAllFrames();
+
+
+ // The following is only there to for compatibility with older saves
+ // resp. the original engine.
+ uint callbackCount;
+ Common::String callbackFunctionName;
+ uint callbackData;
+
+ // loop point callback
+ reader.read(callbackCount);
+ assert(callbackCount == 1);
+ reader.readString(callbackFunctionName);
+ assert(callbackFunctionName == "LuaLoopPointCB");
+ reader.read(callbackData);
+ assert(callbackData == getHandle());
+
+ // loop point callback
+ reader.read(callbackCount);
+ assert(callbackCount == 1);
+ reader.readString(callbackFunctionName);
+ assert(callbackFunctionName == "LuaActionCB");
+ reader.read(callbackData);
+ assert(callbackData == getHandle());
+
+ // loop point callback
+ reader.read(callbackCount);
+ assert(callbackCount == 1);
+ reader.readString(callbackFunctionName);
+ assert(callbackFunctionName == "LuaDeleteCB");
+ reader.read(callbackData);
+ assert(callbackData == getHandle());
+
+ // Set the callbacks
+ setCallbacks();
+
+ result &= RenderObject::unpersistChildren(reader);
+
+ return reader.isGood() && result;
+}
+
+// -----------------------------------------------------------------------------
+
+AnimationDescription *Animation::getAnimationDescription() const {
+ if (_animationResourcePtr)
+ return _animationResourcePtr;
+ else
+ return AnimationTemplateRegistry::instance().resolveHandle(_animationTemplateHandle);
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/gfx/animation.h b/engines/sword25/gfx/animation.h
new file mode 100644
index 0000000000..72fe7e2de8
--- /dev/null
+++ b/engines/sword25/gfx/animation.h
@@ -0,0 +1,219 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#ifndef SWORD25_ANIMATION_H
+#define SWORD25_ANIMATION_H
+
+// Includes
+#include "sword25/kernel/common.h"
+#include "sword25/gfx/timedrenderobject.h"
+
+namespace Sword25 {
+
+// Forward declarations
+class Kernel;
+class AnimationResource;
+class AnimationTemplate;
+class AnimationDescription;
+class InputPersistenceBlock;
+
+class Animation : public TimedRenderObject {
+ friend class RenderObject;
+
+private:
+ Animation(RenderObjectPtr<RenderObject> parentPtr, const Common::String &fileName);
+ Animation(RenderObjectPtr<RenderObject> parentPtr, const AnimationTemplate &template_);
+ Animation(InputPersistenceBlock &reader, RenderObjectPtr<RenderObject> parentPtr, uint handle);
+
+public:
+ enum ANIMATION_TYPES {
+ AT_ONESHOT,
+ AT_LOOP,
+ AT_JOJO
+ };
+
+ virtual ~Animation();
+
+ void play();
+ void pause();
+ void stop();
+ void setFrame(uint nr);
+
+ virtual void setPos(int x, int y);
+ virtual void setX(int x);
+ virtual void setY(int y);
+
+ virtual int getX() const;
+ virtual int getY() const;
+ virtual int getAbsoluteX() const;
+ virtual int getAbsoluteY() const;
+
+ /**
+ @brief Setzt den Alphawert der Animation.
+ @param Alpha der neue Alphawert der Animation (0 = keine Deckung, 255 = volle Deckung).
+ @remark Diese Methode darf nur aufgerufen werden, wenn die Methode IsAlphaAllowed() true zurückgibt.
+ */
+ void setAlpha(int alpha);
+
+ /**
+ @brief Setzt die Modulationfarbe der Animation.
+ @param Color eine 24-Bit Farbe, die die Modulationsfarbe der Animation festlegt.
+ @remark Diese Methode darf nur aufgerufen werden, wenn die Methode IsColorModulationAllowed() true zurückgibt.
+ */
+ void setModulationColor(uint modulationColor);
+
+ /**
+ @brief Setzt den Skalierungsfaktor der Animation.
+ @param ScaleFactor der Faktor um den die Animation in beide Richtungen gestreckt werden soll.
+ @remark Diese Methode darf nur aufgerufen werden, wenn die Methode IsScalingAllowed() true zurückgibt.
+ */
+ void setScaleFactor(float scaleFactor);
+
+ /**
+ @brief Setzt den Skalierungsfaktor der Animation auf der X-Achse.
+ @param ScaleFactor der Faktor um den die Animation in Richtungen der X-Achse gestreckt werden soll.
+ @remark Diese Methode darf nur aufgerufen werden, wenn die Methode IsScalingAllowed() true zurückgibt.
+ */
+ void setScaleFactorX(float scaleFactorX);
+
+ /**
+ @brief Setzt den Skalierungsfaktor der Animation auf der Y-Achse.
+ @param ScaleFactor der Faktor um den die Animation in Richtungen der Y-Achse gestreckt werden soll.
+ @remark Diese Methode darf nur aufgerufen werden, wenn die Methode IsScalingAllowed() true zurückgibt.
+ */
+ void setScaleFactorY(float scaleFactorY);
+
+ /**
+ @brief Gibt den Skalierungsfakter der Animation auf der X-Achse zurück.
+ @remark Diese Methode darf nur aufgerufen werden, wenn die Methode IsScalingAllowed() true zurückgibt.
+ */
+ float getScaleFactorX() const {
+ return _scaleFactorX;
+ }
+
+ /**
+ @brief Gibt den Skalierungsfakter der Animation auf der Y-Achse zurück.
+ @remark Diese Methode darf nur aufgerufen werden, wenn die Methode IsScalingAllowed() true zurückgibt.
+ */
+ float getScaleFactorY() const {
+ return _scaleFactorY;
+ }
+
+ virtual bool persist(OutputPersistenceBlock &writer);
+ virtual bool unpersist(InputPersistenceBlock &reader);
+
+ virtual void frameNotification(int timeElapsed);
+
+ ANIMATION_TYPES getAnimationType() const;
+ int getFPS() const;
+ int getFrameCount() const;
+ bool isScalingAllowed() const;
+ bool isAlphaAllowed() const;
+ bool isColorModulationAllowed() const;
+ uint getCurrentFrame() const {
+ return _currentFrame;
+ }
+ const Common::String &getCurrentAction() const;
+ bool isRunning() const {
+ return _running;
+ }
+
+ typedef bool (*ANIMATION_CALLBACK)(uint);
+
+ void setCallbacks();
+
+protected:
+ virtual bool doRender();
+
+private:
+ enum Direction {
+ FORWARD,
+ BACKWARD
+ };
+
+ int _relX;
+ int _relY;
+ float _scaleFactorX;
+ float _scaleFactorY;
+ uint _modulationColor;
+ uint _currentFrame;
+ int _currentFrameTime;
+ bool _running;
+ bool _finished;
+ Direction _direction;
+ AnimationResource *_animationResourcePtr;
+ uint _animationTemplateHandle;
+ bool _framesLocked;
+
+ ANIMATION_CALLBACK _loopPointCallback;
+ ANIMATION_CALLBACK _actionCallback;
+ ANIMATION_CALLBACK _deleteCallback;
+
+ /**
+ @brief Lockt alle Frames.
+ @return Gibt false zurück, falls nicht alle Frames gelockt werden konnten.
+ */
+ bool lockAllFrames();
+
+ /**
+ @brief Unlockt alle Frames.
+ @return Gibt false zurück, falls nicht alles Frames freigegeben werden konnten.
+ */
+ bool unlockAllFrames();
+
+ /**
+ @brief Diese Methode aktualisiert die Parameter (Größe, Position) der Animation anhand des aktuellen Frames.
+
+ Diese Methode muss bei jedem Framewechsel aufgerufen werden damit der RenderObject-Manager immer aktuelle Daten hat.
+ */
+ void computeCurrentCharacteristics();
+
+ /**
+ @brief Berechnet den Abstand zwischen dem linken Rand und dem Hotspot auf X-Achse in der aktuellen Darstellung.
+ */
+ int computeXModifier() const;
+
+ /**
+ @brief Berechnet den Abstand zwischen dem linken Rand und dem Hotspot auf X-Achse in der aktuellen Darstellung.
+ */
+ int computeYModifier() const;
+
+ void initMembers();
+ AnimationDescription *getAnimationDescription() const;
+ void initializeAnimationResource(const Common::String &fileName);
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/gfx/animationdescription.cpp b/engines/sword25/gfx/animationdescription.cpp
new file mode 100644
index 0000000000..68ba7b63a6
--- /dev/null
+++ b/engines/sword25/gfx/animationdescription.cpp
@@ -0,0 +1,65 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#include "sword25/kernel/outputpersistenceblock.h"
+#include "sword25/kernel/inputpersistenceblock.h"
+#include "sword25/gfx/animationdescription.h"
+
+namespace Sword25 {
+
+bool AnimationDescription::persist(OutputPersistenceBlock &writer) {
+ writer.write(static_cast<uint>(_animationType));
+ writer.write(_FPS);
+ writer.write(_millisPerFrame);
+ writer.write(_scalingAllowed);
+ writer.write(_alphaAllowed);
+ writer.write(_colorModulationAllowed);
+
+ return true;
+}
+
+bool AnimationDescription::unpersist(InputPersistenceBlock &reader) {
+ uint animationType;
+ reader.read(animationType);
+ _animationType = static_cast<Animation::ANIMATION_TYPES>(animationType);
+ reader.read(_FPS);
+ reader.read(_millisPerFrame);
+ reader.read(_scalingAllowed);
+ reader.read(_alphaAllowed);
+ reader.read(_colorModulationAllowed);
+
+ return reader.isGood();
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/gfx/animationdescription.h b/engines/sword25/gfx/animationdescription.h
new file mode 100644
index 0000000000..88cbb23503
--- /dev/null
+++ b/engines/sword25/gfx/animationdescription.h
@@ -0,0 +1,103 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#ifndef SWORD25_ANIMATIONDESCRIPTION_H
+#define SWORD25_ANIMATIONDESCRIPTION_H
+
+#include "sword25/kernel/common.h"
+#include "sword25/kernel/persistable.h"
+#include "sword25/gfx/animation.h"
+
+namespace Sword25 {
+
+class AnimationDescription : public Persistable {
+protected:
+ AnimationDescription() :
+ _animationType(Animation::AT_LOOP),
+ _FPS(10),
+ _millisPerFrame(0),
+ _scalingAllowed(true),
+ _alphaAllowed(true),
+ _colorModulationAllowed(true)
+ {}
+
+public:
+ struct Frame {
+ // Die Hotspot-Angabe bezieht sich auf das ungeflippte Bild!!
+ int hotspotX;
+ int hotspotY;
+ bool flipV;
+ bool flipH;
+ Common::String fileName;
+ Common::String action;
+ };
+
+ virtual const Frame &getFrame(uint index) const = 0;
+ virtual uint getFrameCount() const = 0;
+ virtual void unlock() = 0;
+
+ Animation::ANIMATION_TYPES getAnimationType() const {
+ return _animationType;
+ }
+ int getFPS() const {
+ return _FPS;
+ }
+ int getMillisPerFrame() const {
+ return _millisPerFrame;
+ }
+ bool isScalingAllowed() const {
+ return _scalingAllowed;
+ }
+ bool isAlphaAllowed() const {
+ return _alphaAllowed;
+ }
+ bool isColorModulationAllowed() const {
+ return _colorModulationAllowed;
+ }
+
+ virtual bool persist(OutputPersistenceBlock &writer);
+ virtual bool unpersist(InputPersistenceBlock &reader);
+
+protected:
+ Animation::ANIMATION_TYPES _animationType;
+ int _FPS;
+ int _millisPerFrame;
+ bool _scalingAllowed;
+ bool _alphaAllowed;
+ bool _colorModulationAllowed;
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/gfx/animationresource.cpp b/engines/sword25/gfx/animationresource.cpp
new file mode 100644
index 0000000000..6e5f683a2e
--- /dev/null
+++ b/engines/sword25/gfx/animationresource.cpp
@@ -0,0 +1,252 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#include "sword25/gfx/animationresource.h"
+
+#include "sword25/kernel/kernel.h"
+#include "sword25/package/packagemanager.h"
+#include "sword25/gfx/bitmapresource.h"
+
+namespace Sword25 {
+
+#define BS_LOG_PREFIX "ANIMATIONRESOURCE"
+
+namespace {
+const int DEFAULT_FPS = 10;
+const int MIN_FPS = 1;
+const int MAX_FPS = 200;
+}
+
+AnimationResource::AnimationResource(const Common::String &filename) :
+ Resource(filename, Resource::TYPE_ANIMATION),
+ Common::XMLParser(),
+ _valid(false) {
+ // Get a pointer to the package manager
+ _pPackage = Kernel::getInstance()->getPackage();
+ BS_ASSERT(_pPackage);
+
+ // Switch to the folder the specified Xml fiile is in
+ Common::String oldDirectory = _pPackage->getCurrentDirectory();
+ if (getFileName().contains('/')) {
+ Common::String dir = Common::String(getFileName().c_str(), strrchr(getFileName().c_str(), '/'));
+ _pPackage->changeDirectory(dir);
+ }
+
+ // Load the contents of the file
+ uint fileSize;
+ char *xmlData = _pPackage->getXmlFile(getFileName(), &fileSize);
+ if (!xmlData) {
+ BS_LOG_ERRORLN("Could not read \"%s\".", getFileName().c_str());
+ return;
+ }
+
+ // Parse the contents
+ if (!loadBuffer((const byte *)xmlData, fileSize))
+ return;
+
+ _valid = parse();
+ close();
+ free(xmlData);
+
+ // Switch back to the previous folder
+ _pPackage->changeDirectory(oldDirectory);
+
+ // Give an error message if there weren't any frames specified
+ if (_frames.empty()) {
+ BS_LOG_ERRORLN("\"%s\" does not have any frames.", getFileName().c_str());
+ return;
+ }
+
+ // Pre-cache all the frames
+ if (!precacheAllFrames()) {
+ BS_LOG_ERRORLN("Could not precache all frames of \"%s\".", getFileName().c_str());
+ return;
+ }
+
+ // Post processing to compute animation features
+ if (!computeFeatures()) {
+ BS_LOG_ERRORLN("Could not determine the features of \"%s\".", getFileName().c_str());
+ return;
+ }
+
+ _valid = true;
+}
+
+bool AnimationResource::parseBooleanKey(Common::String s, bool &result) {
+ s.toLowercase();
+ if (!strcmp(s.c_str(), "true"))
+ result = true;
+ else if (!strcmp(s.c_str(), "false"))
+ result = false;
+ else
+ return false;
+ return true;
+}
+
+bool AnimationResource::parserCallback_animation(ParserNode *node) {
+ if (!parseIntegerKey(node->values["fps"], 1, &_FPS) || (_FPS < MIN_FPS) || (_FPS > MAX_FPS)) {
+ return parserError("Illegal or missing fps attribute in <animation> tag in \"%s\". Assuming default (\"%d\").",
+ getFileName().c_str(), DEFAULT_FPS);
+ }
+
+ // Loop type value
+ const char *loopTypeString = node->values["type"].c_str();
+
+ if (strcmp(loopTypeString, "oneshot") == 0) {
+ _animationType = Animation::AT_ONESHOT;
+ } else if (strcmp(loopTypeString, "loop") == 0) {
+ _animationType = Animation::AT_LOOP;
+ } else if (strcmp(loopTypeString, "jojo") == 0) {
+ _animationType = Animation::AT_JOJO;
+ } else {
+ BS_LOG_WARNINGLN("Illegal type value (\"%s\") in <animation> tag in \"%s\". Assuming default (\"loop\").",
+ loopTypeString, getFileName().c_str());
+ _animationType = Animation::AT_LOOP;
+ }
+
+ // Calculate the milliseconds required per frame
+ // FIXME: Double check variable naming. Based on the constant, it may be microseconds
+ _millisPerFrame = 1000000 / _FPS;
+
+ return true;
+}
+
+bool AnimationResource::parserCallback_frame(ParserNode *node) {
+ Frame frame;
+
+ const char *fileString = node->values["file"].c_str();
+ if (!fileString) {
+ BS_LOG_ERRORLN("<frame> tag without file attribute occurred in \"%s\".", getFileName().c_str());
+ return false;
+ }
+ frame.fileName = _pPackage->getAbsolutePath(fileString);
+ if (frame.fileName.empty()) {
+ BS_LOG_ERRORLN("Could not create absolute path for file specified in <frame> tag in \"%s\": \"%s\".",
+ getFileName().c_str(), fileString);
+ return false;
+ }
+
+ const char *actionString = node->values["action"].c_str();
+ if (actionString)
+ frame.action = actionString;
+
+ const char *hotspotxString = node->values["hotspotx"].c_str();
+ const char *hotspotyString = node->values["hotspoty"].c_str();
+ if ((!hotspotxString && hotspotyString) ||
+ (hotspotxString && !hotspotyString))
+ BS_LOG_WARNINGLN("%s attribute occurred without %s attribute in <frame> tag in \"%s\". Assuming default (\"0\").",
+ hotspotxString ? "hotspotx" : "hotspoty",
+ !hotspotyString ? "hotspoty" : "hotspotx",
+ getFileName().c_str());
+
+ frame.hotspotX = 0;
+ if (hotspotxString && !parseIntegerKey(hotspotxString, 1, &frame.hotspotX))
+ BS_LOG_WARNINGLN("Illegal hotspotx value (\"%s\") in frame tag in \"%s\". Assuming default (\"%s\").",
+ hotspotxString, getFileName().c_str(), frame.hotspotX);
+
+ frame.hotspotY = 0;
+ if (hotspotyString && !parseIntegerKey(hotspotyString, 1, &frame.hotspotY))
+ BS_LOG_WARNINGLN("Illegal hotspoty value (\"%s\") in frame tag in \"%s\". Assuming default (\"%s\").",
+ hotspotyString, getFileName().c_str(), frame.hotspotY);
+
+ Common::String flipVString = node->values["flipv"];
+ if (!flipVString.empty()) {
+ if (!parseBooleanKey(flipVString, frame.flipV)) {
+ BS_LOG_WARNINGLN("Illegal flipv value (\"%s\") in <frame> tag in \"%s\". Assuming default (\"false\").",
+ flipVString.c_str(), getFileName().c_str());
+ frame.flipV = false;
+ }
+ } else
+ frame.flipV = false;
+
+ Common::String flipHString = node->values["fliph"];
+ if (!flipHString.empty()) {
+ if (!parseBooleanKey(flipVString, frame.flipV)) {
+ BS_LOG_WARNINGLN("Illegal fliph value (\"%s\") in <frame> tag in \"%s\". Assuming default (\"false\").",
+ flipHString.c_str(), getFileName().c_str());
+ frame.flipH = false;
+ }
+ } else
+ frame.flipH = false;
+
+ _frames.push_back(frame);
+ return true;
+}
+
+AnimationResource::~AnimationResource() {
+}
+
+bool AnimationResource::precacheAllFrames() const {
+ Common::Array<Frame>::const_iterator iter = _frames.begin();
+ for (; iter != _frames.end(); ++iter) {
+ if (!Kernel::getInstance()->getResourceManager()->precacheResource((*iter).fileName)) {
+ BS_LOG_ERRORLN("Could not precache \"%s\".", (*iter).fileName.c_str());
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool AnimationResource::computeFeatures() {
+ BS_ASSERT(_frames.size());
+
+ // Alle Features werden als vorhanden angenommen
+ _scalingAllowed = true;
+ _alphaAllowed = true;
+ _colorModulationAllowed = true;
+
+ // Alle Frame durchgehen und alle Features deaktivieren, die auch nur von einem Frame nicht unterstützt werden.
+ Common::Array<Frame>::const_iterator iter = _frames.begin();
+ for (; iter != _frames.end(); ++iter) {
+ BitmapResource *pBitmap;
+ if (!(pBitmap = static_cast<BitmapResource *>(Kernel::getInstance()->getResourceManager()->requestResource((*iter).fileName)))) {
+ BS_LOG_ERRORLN("Could not request \"%s\".", (*iter).fileName.c_str());
+ return false;
+ }
+
+ if (!pBitmap->isScalingAllowed())
+ _scalingAllowed = false;
+ if (!pBitmap->isAlphaAllowed())
+ _alphaAllowed = false;
+ if (!pBitmap->isColorModulationAllowed())
+ _colorModulationAllowed = false;
+
+ pBitmap->release();
+ }
+
+ return true;
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/gfx/animationresource.h b/engines/sword25/gfx/animationresource.h
new file mode 100644
index 0000000000..da07b55c3b
--- /dev/null
+++ b/engines/sword25/gfx/animationresource.h
@@ -0,0 +1,123 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#ifndef SWORD25_ANIMATIONRESOURCE_H
+#define SWORD25_ANIMATIONRESOURCE_H
+
+#include "common/xmlparser.h"
+#include "sword25/kernel/common.h"
+#include "sword25/kernel/resource.h"
+#include "sword25/gfx/animationdescription.h"
+#include "sword25/gfx/animation.h"
+
+namespace Sword25 {
+
+class Kernel;
+class PackageManager;
+
+class AnimationResource : public Resource, public AnimationDescription, public Common::XMLParser {
+public:
+ AnimationResource(const Common::String &filename);
+ virtual ~AnimationResource();
+
+ virtual const Frame &getFrame(uint index) const {
+ BS_ASSERT(index < _frames.size());
+ return _frames[index];
+ }
+ virtual uint getFrameCount() const {
+ return _frames.size();
+ }
+ virtual void unlock() {
+ release();
+ }
+
+ Animation::ANIMATION_TYPES getAnimationType() const {
+ return _animationType;
+ }
+ int getFPS() const {
+ return _FPS;
+ }
+ int getMillisPerFrame() const {
+ return _millisPerFrame;
+ }
+ bool isScalingAllowed() const {
+ return _scalingAllowed;
+ }
+ bool isAlphaAllowed() const {
+ return _alphaAllowed;
+ }
+ bool isColorModulationAllowed() const {
+ return _colorModulationAllowed;
+ }
+ bool isValid() const {
+ return _valid;
+ }
+
+private:
+ bool _valid;
+
+ Common::Array<Frame> _frames;
+
+ PackageManager *_pPackage;
+
+
+ bool computeFeatures();
+ bool precacheAllFrames() const;
+
+ // Parser
+ CUSTOM_XML_PARSER(AnimationResource) {
+ XML_KEY(animation)
+ XML_PROP(fps, true)
+ XML_PROP(type, true)
+
+ XML_KEY(frame)
+ XML_PROP(file, true)
+ XML_PROP(hotspotx, true)
+ XML_PROP(hotspoty, true)
+ XML_PROP(fliph, false)
+ XML_PROP(flipv, false)
+ KEY_END()
+ KEY_END()
+ } PARSER_END()
+
+ bool parseBooleanKey(Common::String s, bool &result);
+
+ // Parser callback methods
+ bool parserCallback_animation(ParserNode *node);
+ bool parserCallback_frame(ParserNode *node);
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/gfx/animationtemplate.cpp b/engines/sword25/gfx/animationtemplate.cpp
new file mode 100644
index 0000000000..4a060dbad9
--- /dev/null
+++ b/engines/sword25/gfx/animationtemplate.cpp
@@ -0,0 +1,243 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#define BS_LOG_PREFIX "ANIMATIONTEMPLATE"
+
+#include "sword25/kernel/kernel.h"
+#include "sword25/kernel/resource.h"
+#include "sword25/kernel/outputpersistenceblock.h"
+#include "sword25/kernel/inputpersistenceblock.h"
+
+#include "sword25/gfx/animationresource.h"
+#include "sword25/gfx/animationtemplate.h"
+#include "sword25/gfx/animationtemplateregistry.h"
+
+namespace Sword25 {
+
+uint AnimationTemplate::create(const Common::String &sourceAnimation) {
+ AnimationTemplate *animationTemplatePtr = new AnimationTemplate(sourceAnimation);
+
+ if (animationTemplatePtr->isValid()) {
+ return AnimationTemplateRegistry::instance().resolvePtr(animationTemplatePtr);
+ } else {
+ delete animationTemplatePtr;
+ return 0;
+ }
+}
+
+uint AnimationTemplate::create(const AnimationTemplate &other) {
+ AnimationTemplate *animationTemplatePtr = new AnimationTemplate(other);
+
+ if (animationTemplatePtr->isValid()) {
+ return AnimationTemplateRegistry::instance().resolvePtr(animationTemplatePtr);
+ } else {
+ delete animationTemplatePtr;
+ return 0;
+ }
+}
+
+uint AnimationTemplate::create(InputPersistenceBlock &reader, uint handle) {
+ AnimationTemplate *animationTemplatePtr = new AnimationTemplate(reader, handle);
+
+ if (animationTemplatePtr->isValid()) {
+ return AnimationTemplateRegistry::instance().resolvePtr(animationTemplatePtr);
+ } else {
+ delete animationTemplatePtr;
+ return 0;
+ }
+}
+
+AnimationTemplate::AnimationTemplate(const Common::String &sourceAnimation) {
+ // Objekt registrieren.
+ AnimationTemplateRegistry::instance().registerObject(this);
+
+ _valid = false;
+
+ // Die Animations-Resource wird für die gesamte Lebensdauer des Objektes gelockt
+ _sourceAnimationPtr = requestSourceAnimation(sourceAnimation);
+
+ // Erfolg signalisieren
+ _valid = (_sourceAnimationPtr != 0);
+}
+
+AnimationTemplate::AnimationTemplate(const AnimationTemplate &other) : AnimationDescription() {
+ // Objekt registrieren.
+ AnimationTemplateRegistry::instance().registerObject(this);
+
+ _valid = false;
+
+ // Die Animations-Resource wird für die gesamte Lebensdauer des Objektes gelockt.
+ if (!other._sourceAnimationPtr)
+ return;
+ _sourceAnimationPtr = requestSourceAnimation(other._sourceAnimationPtr->getFileName());
+
+ // Alle Member kopieren.
+ _animationType = other._animationType;
+ _FPS = other._FPS;
+ _millisPerFrame = other._millisPerFrame;
+ _scalingAllowed = other._scalingAllowed;
+ _alphaAllowed = other._alphaAllowed;
+ _colorModulationAllowed = other._colorModulationAllowed;
+ _frames = other._frames;
+ _sourceAnimationPtr = other._sourceAnimationPtr;
+ _valid = other._valid;
+
+ _valid &= (_sourceAnimationPtr != 0);
+}
+
+AnimationTemplate::AnimationTemplate(InputPersistenceBlock &reader, uint handle) {
+ // Objekt registrieren.
+ AnimationTemplateRegistry::instance().registerObject(this, handle);
+
+ // Objekt laden.
+ _valid = unpersist(reader);
+}
+
+AnimationResource *AnimationTemplate::requestSourceAnimation(const Common::String &sourceAnimation) const {
+ ResourceManager *RMPtr = Kernel::getInstance()->getResourceManager();
+ Resource *resourcePtr;
+ if (NULL == (resourcePtr = RMPtr->requestResource(sourceAnimation)) || resourcePtr->getType() != Resource::TYPE_ANIMATION) {
+ BS_LOG_ERRORLN("The resource \"%s\" could not be requested or is has an invalid type. The animation template can't be created.", sourceAnimation.c_str());
+ return 0;
+ }
+ return static_cast<AnimationResource *>(resourcePtr);
+}
+
+AnimationTemplate::~AnimationTemplate() {
+ // Animations-Resource freigeben
+ if (_sourceAnimationPtr) {
+ _sourceAnimationPtr->release();
+ }
+
+ // Objekt deregistrieren
+ AnimationTemplateRegistry::instance().deregisterObject(this);
+}
+
+void AnimationTemplate::addFrame(int index) {
+ if (validateSourceIndex(index)) {
+ _frames.push_back(_sourceAnimationPtr->getFrame(index));
+ }
+}
+
+void AnimationTemplate::setFrame(int destIndex, int srcIndex) {
+ if (validateDestIndex(destIndex) && validateSourceIndex(srcIndex)) {
+ _frames[destIndex] = _sourceAnimationPtr->getFrame(srcIndex);
+ }
+}
+
+bool AnimationTemplate::validateSourceIndex(uint index) const {
+ if (index > _sourceAnimationPtr->getFrameCount()) {
+ BS_LOG_WARNINGLN("Tried to insert a frame (\"%d\") that does not exist in the source animation (\"%s\"). Ignoring call.",
+ index, _sourceAnimationPtr->getFileName().c_str());
+ return false;
+ } else
+ return true;
+}
+
+bool AnimationTemplate::validateDestIndex(uint index) const {
+ if (index > _frames.size()) {
+ BS_LOG_WARNINGLN("Tried to change a nonexistent frame (\"%d\") in a template animation. Ignoring call.",
+ index);
+ return false;
+ } else
+ return true;
+}
+
+void AnimationTemplate::setFPS(int FPS) {
+ _FPS = FPS;
+ _millisPerFrame = 1000000 / _FPS;
+}
+
+bool AnimationTemplate::persist(OutputPersistenceBlock &writer) {
+ bool Result = true;
+
+ // Parent persistieren.
+ Result &= AnimationDescription::persist(writer);
+
+ // Frameanzahl schreiben.
+ writer.write(_frames.size());
+
+ // Frames einzeln persistieren.
+ Common::Array<const Frame>::const_iterator Iter = _frames.begin();
+ while (Iter != _frames.end()) {
+ writer.write(Iter->hotspotX);
+ writer.write(Iter->hotspotY);
+ writer.write(Iter->flipV);
+ writer.write(Iter->flipH);
+ writer.writeString(Iter->fileName);
+ writer.writeString(Iter->action);
+ ++Iter;
+ }
+
+ // Restliche Member persistieren.
+ writer.writeString(_sourceAnimationPtr->getFileName());
+ writer.write(_valid);
+
+ return Result;
+}
+
+bool AnimationTemplate::unpersist(InputPersistenceBlock &reader) {
+ bool result = true;
+
+ // Parent wieder herstellen.
+ result &= AnimationDescription::unpersist(reader);
+
+ // Frameanzahl lesen.
+ uint frameCount;
+ reader.read(frameCount);
+
+ // Frames einzeln wieder herstellen.
+ for (uint i = 0; i < frameCount; ++i) {
+ Frame frame;
+ reader.read(frame.hotspotX);
+ reader.read(frame.hotspotY);
+ reader.read(frame.flipV);
+ reader.read(frame.flipH);
+ reader.readString(frame.fileName);
+ reader.readString(frame.action);
+
+ _frames.push_back(frame);
+ }
+
+ // Die Animations-Resource wird für die gesamte Lebensdauer des Objektes gelockt
+ Common::String sourceAnimation;
+ reader.readString(sourceAnimation);
+ _sourceAnimationPtr = requestSourceAnimation(sourceAnimation);
+
+ reader.read(_valid);
+
+ return _sourceAnimationPtr && reader.isGood() && result;
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/gfx/animationtemplate.h b/engines/sword25/gfx/animationtemplate.h
new file mode 100644
index 0000000000..294f249f81
--- /dev/null
+++ b/engines/sword25/gfx/animationtemplate.h
@@ -0,0 +1,125 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#ifndef SWORD25_ANIMATION_TEMPLATE_H
+#define SWORD25_ANIMATION_TEMPLATE_H
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "sword25/kernel/common.h"
+#include "sword25/kernel/persistable.h"
+#include "sword25/gfx/animationdescription.h"
+
+namespace Sword25 {
+
+class AnimationResource;
+
+class AnimationTemplate : public AnimationDescription {
+public:
+ static uint create(const Common::String &sourceAnimation);
+ static uint create(const AnimationTemplate &other);
+ static uint create(InputPersistenceBlock &reader, uint handle);
+ AnimationTemplate *resolveHandle(uint handle) const;
+
+private:
+ AnimationTemplate(const Common::String &sourceAnimation);
+ AnimationTemplate(const AnimationTemplate &other);
+ AnimationTemplate(InputPersistenceBlock &reader, uint handle);
+
+public:
+ ~AnimationTemplate();
+
+ virtual const Frame &getFrame(uint index) const {
+ BS_ASSERT(index < _frames.size());
+ return _frames[index];
+ }
+ virtual uint getFrameCount() const {
+ return _frames.size();
+ }
+ virtual void unlock() {
+ delete this;
+ }
+
+ bool isValid() const {
+ return _valid;
+ }
+
+ /**
+ @brief Fügt einen neuen Frame zur Animation hinzu.
+
+ Der Frame wird an das Ende der Animation angehängt.
+
+ @param Index der Index des Frames in der Quellanimation
+ */
+ void addFrame(int index);
+
+ /**
+ @brief Ändert einen bereits in der Animation vorhandenen Frame.
+ @param DestIndex der Index des Frames der überschrieben werden soll
+ @param SrcIndex der Index des einzufügenden Frames in der Quellanimation
+ */
+ void setFrame(int destIndex, int srcIndex);
+
+ /**
+ @brief Setzt den Animationstyp.
+ @param Type der Typ der Animation. Muss aus den enum BS_Animation::ANIMATION_TYPES sein.
+ */
+ void setAnimationType(Animation::ANIMATION_TYPES type) {
+ _animationType = type;
+ }
+
+ /**
+ @brief Setzt die Abspielgeschwindigkeit.
+ @param FPS die Abspielgeschwindigkeit in Frames pro Sekunde.
+ */
+ void setFPS(int FPS);
+
+ virtual bool persist(OutputPersistenceBlock &writer);
+ virtual bool unpersist(InputPersistenceBlock &reader);
+
+private:
+ Common::Array<Frame> _frames;
+ AnimationResource *_sourceAnimationPtr;
+ bool _valid;
+
+ AnimationResource *requestSourceAnimation(const Common::String &sourceAnimation) const;
+ bool validateSourceIndex(uint index) const;
+ bool validateDestIndex(uint index) const;
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/gfx/animationtemplateregistry.cpp b/engines/sword25/gfx/animationtemplateregistry.cpp
new file mode 100644
index 0000000000..ac1d6096f4
--- /dev/null
+++ b/engines/sword25/gfx/animationtemplateregistry.cpp
@@ -0,0 +1,107 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#define BS_LOG_PREFIX "ANIMATIONTEMPLATEREGISTRY"
+
+#include "sword25/kernel/outputpersistenceblock.h"
+#include "sword25/kernel/inputpersistenceblock.h"
+#include "sword25/gfx/animationtemplateregistry.h"
+#include "sword25/gfx/animationtemplate.h"
+
+DECLARE_SINGLETON(Sword25::AnimationTemplateRegistry)
+
+namespace Sword25 {
+
+void AnimationTemplateRegistry::logErrorLn(const char *message) const {
+ BS_LOG_ERRORLN(message);
+}
+
+void AnimationTemplateRegistry::logWarningLn(const char *message) const {
+ BS_LOG_WARNINGLN(message);
+}
+
+bool AnimationTemplateRegistry::persist(OutputPersistenceBlock &writer) {
+ bool result = true;
+
+ // Das nächste zu vergebene Handle schreiben.
+ writer.write(_nextHandle);
+
+ // Anzahl an BS_AnimationTemplates schreiben.
+ writer.write(_handle2PtrMap.size());
+
+ // Alle BS_AnimationTemplates persistieren.
+ HANDLE2PTR_MAP::const_iterator iter = _handle2PtrMap.begin();
+ while (iter != _handle2PtrMap.end()) {
+ // Handle persistieren.
+ writer.write(iter->_key);
+
+ // Objekt persistieren.
+ result &= iter->_value->persist(writer);
+
+ ++iter;
+ }
+
+ return result;
+}
+
+// -----------------------------------------------------------------------------
+
+bool AnimationTemplateRegistry::unpersist(InputPersistenceBlock &reader) {
+ bool result = true;
+
+ // Das nächste zu vergebene Handle wieder herstellen.
+ reader.read(_nextHandle);
+
+ // Alle vorhandenen BS_AnimationTemplates zerstören.
+ while (!_handle2PtrMap.empty())
+ delete _handle2PtrMap.begin()->_value;
+
+ // Anzahl an BS_AnimationTemplates einlesen.
+ uint animationTemplateCount;
+ reader.read(animationTemplateCount);
+
+ // Alle gespeicherten BS_AnimationTemplates wieder herstellen.
+ for (uint i = 0; i < animationTemplateCount; ++i) {
+ // Handle lesen.
+ uint handle;
+ reader.read(handle);
+
+ // BS_AnimationTemplate wieder herstellen.
+ result &= (AnimationTemplate::create(reader, handle) != 0);
+ }
+
+ return reader.isGood() && result;
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/gfx/animationtemplateregistry.h b/engines/sword25/gfx/animationtemplateregistry.h
new file mode 100644
index 0000000000..c5308bb124
--- /dev/null
+++ b/engines/sword25/gfx/animationtemplateregistry.h
@@ -0,0 +1,64 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#ifndef SWORD25_ANIMATIONTEMPLATEREGISTRY_H
+#define SWORD25_ANIMATIONTEMPLATEREGISTRY_H
+
+#include "sword25/kernel/common.h"
+#include "sword25/kernel/persistable.h"
+#include "sword25/kernel/objectregistry.h"
+
+#include "common/singleton.h"
+
+namespace Sword25 {
+
+class AnimationTemplate;
+
+class AnimationTemplateRegistry :
+ public ObjectRegistry<AnimationTemplate>,
+ public Persistable,
+ public Common::Singleton<AnimationTemplateRegistry> {
+public:
+
+ virtual bool persist(OutputPersistenceBlock &writer);
+ virtual bool unpersist(InputPersistenceBlock &reader);
+
+private:
+ virtual void logErrorLn(const char *message) const;
+ virtual void logWarningLn(const char *message) const;
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/gfx/bitmap.cpp b/engines/sword25/gfx/bitmap.cpp
new file mode 100644
index 0000000000..b5412c8276
--- /dev/null
+++ b/engines/sword25/gfx/bitmap.cpp
@@ -0,0 +1,183 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#include "sword25/gfx/bitmap.h"
+#include "sword25/kernel/outputpersistenceblock.h"
+#include "sword25/kernel/inputpersistenceblock.h"
+
+namespace Sword25 {
+
+#define BS_LOG_PREFIX "BITMAP"
+
+Bitmap::Bitmap(RenderObjectPtr<RenderObject> parentPtr, TYPES type, uint handle) :
+ RenderObject(parentPtr, type, handle),
+ _modulationColor(0xffffffff),
+ _scaleFactorX(1.0f),
+ _scaleFactorY(1.0f),
+ _flipH(false),
+ _flipV(false) {
+}
+
+Bitmap::~Bitmap() {
+}
+
+void Bitmap::setAlpha(int alpha) {
+ if (!isAlphaAllowed()) {
+ BS_LOG_WARNINGLN("Tried to set alpha value on a bitmap that does not support alpha blending. Call was ignored.");
+ return;
+ }
+
+ if (alpha < 0 || alpha > 255) {
+ int oldAlpha = alpha;
+ if (alpha < 0)
+ alpha = 0;
+ if (alpha > 255)
+ alpha = 255;
+ BS_LOG_WARNINGLN("Tried to set an invalid alpha value (%d) on a bitmap. Value was changed to %d.", oldAlpha, alpha);
+
+ return;
+ }
+
+ uint newModulationColor = (_modulationColor & 0x00ffffff) | alpha << 24;
+ if (newModulationColor != _modulationColor) {
+ _modulationColor = newModulationColor;
+ forceRefresh();
+ }
+}
+
+void Bitmap::setModulationColor(uint modulationColor) {
+ if (!isColorModulationAllowed()) {
+ BS_LOG_WARNINGLN("Tried to set modulation color of a bitmap that does not support color modulation. Call was ignored.");
+ return;
+ }
+
+ uint newModulationColor = (modulationColor & 0x00ffffff) | (_modulationColor & 0xff000000);
+ if (newModulationColor != _modulationColor) {
+ _modulationColor = newModulationColor;
+ forceRefresh();
+ }
+}
+
+void Bitmap::setScaleFactor(float scaleFactor) {
+ setScaleFactorX(scaleFactor);
+ setScaleFactorY(scaleFactor);
+}
+
+void Bitmap::setScaleFactorX(float scaleFactorX) {
+ if (!isScalingAllowed()) {
+ BS_LOG_WARNINGLN("Tried to set scale factor of a bitmap that does not support scaling. Call was ignored.");
+ return;
+ }
+
+ if (scaleFactorX < 0) {
+ BS_LOG_WARNINGLN("Tried to set scale factor of a bitmap to a negative value. Call was ignored.");
+ return;
+ }
+
+ if (scaleFactorX != _scaleFactorX) {
+ _scaleFactorX = scaleFactorX;
+ _width = static_cast<int>(_originalWidth * _scaleFactorX);
+ if (_scaleFactorX <= 0.0f)
+ _scaleFactorX = 0.001f;
+ if (_width <= 0)
+ _width = 1;
+ forceRefresh();
+ }
+}
+
+void Bitmap::setScaleFactorY(float scaleFactorY) {
+ if (!isScalingAllowed()) {
+ BS_LOG_WARNINGLN("Tried to set scale factor of a bitmap that does not support scaling. Call was ignored.");
+ return;
+ }
+
+ if (scaleFactorY < 0) {
+ BS_LOG_WARNINGLN("Tried to set scale factor of a bitmap to a negative value. Call was ignored.");
+ return;
+ }
+
+ if (scaleFactorY != _scaleFactorY) {
+ _scaleFactorY = scaleFactorY;
+ _height = static_cast<int>(_originalHeight * scaleFactorY);
+ if (_scaleFactorY <= 0.0f)
+ _scaleFactorY = 0.001f;
+ if (_height <= 0)
+ _height = 1;
+ forceRefresh();
+ }
+}
+
+void Bitmap::setFlipH(bool flipH) {
+ _flipH = flipH;
+ forceRefresh();
+}
+
+void Bitmap::setFlipV(bool flipV) {
+ _flipV = flipV;
+ forceRefresh();
+}
+
+bool Bitmap::persist(OutputPersistenceBlock &writer) {
+ bool result = true;
+
+ result &= RenderObject::persist(writer);
+ writer.write(_flipH);
+ writer.write(_flipV);
+ writer.write(_scaleFactorX);
+ writer.write(_scaleFactorY);
+ writer.write(_modulationColor);
+ writer.write(_originalWidth);
+ writer.write(_originalHeight);
+
+ return result;
+}
+
+bool Bitmap::unpersist(InputPersistenceBlock &reader) {
+ bool result = true;
+
+ result &= RenderObject::unpersist(reader);
+ reader.read(_flipH);
+ reader.read(_flipV);
+ reader.read(_scaleFactorX);
+ reader.read(_scaleFactorY);
+ reader.read(_modulationColor);
+ reader.read(_originalWidth);
+ reader.read(_originalHeight);
+
+ forceRefresh();
+
+ return reader.isGood() && result;
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/gfx/bitmap.h b/engines/sword25/gfx/bitmap.h
new file mode 100644
index 0000000000..741269c423
--- /dev/null
+++ b/engines/sword25/gfx/bitmap.h
@@ -0,0 +1,189 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#ifndef SWORD25_BITMAP_H
+#define SWORD25_BITMAP_H
+
+#include "sword25/kernel/common.h"
+#include "sword25/gfx/renderobject.h"
+
+namespace Sword25 {
+
+class Bitmap : public RenderObject {
+protected:
+ Bitmap(RenderObjectPtr<RenderObject> parentPtr, TYPES type, uint handle = 0);
+
+public:
+
+ virtual ~Bitmap();
+
+ /**
+ @brief Setzt den Alphawert des Bitmaps.
+ @param Alpha der neue Alphawert der Bitmaps (0 = keine Deckung, 255 = volle Deckung).
+ @remark Diese Methode darf nur aufgerufen werden, wenn die Methode IsAlphaAllowed() true zurückgibt.
+ */
+ void setAlpha(int alpha);
+
+ /**
+ @brief Setzt die Modulationfarbe der Bitmaps.
+ @param Color eine 24-Bit Farbe, die die Modulationsfarbe des Bitmaps festlegt.
+ @remark Diese Methode darf nur aufgerufen werden, wenn die Methode IsColorModulationAllowed() true zurückgibt.
+ */
+ void setModulationColor(uint modulationColor);
+
+ /**
+ @brief Setzt den Skalierungsfaktor des Bitmaps.
+ @param ScaleFactor der Faktor um den das Bitmap in beide Richtungen gestreckt werden soll.
+ @remark Diese Methode darf nur aufgerufen werden, wenn die Methode IsScalingAllowed() true zurückgibt.
+ */
+ void setScaleFactor(float scaleFactor);
+
+ /**
+ @brief Setzt den Skalierungsfaktor der Bitmap auf der X-Achse.
+ @param ScaleFactor der Faktor um den die Bitmap in Richtungen der X-Achse gestreckt werden soll. Dieser Wert muss positiv sein.
+ @remark Diese Methode darf nur aufgerufen werden, wenn die Methode IsScalingAllowed() true zurückgibt.
+ */
+ void setScaleFactorX(float scaleFactorX);
+
+ /**
+ @brief Setzt den Skalierungsfaktor der Bitmap auf der Y-Achse.
+ @param ScaleFactor der Faktor um den die Bitmap in Richtungen der Y-Achse gestreckt werden soll. Dieser Wert muss positiv sein.
+ @remark Diese Methode darf nur aufgerufen werden, wenn die Methode IsScalingAllowed() true zurückgibt.
+ */
+ void setScaleFactorY(float scaleFactorY);
+
+ /**
+ @brief Legt fest, ob das Bild an der X-Achse gespiegelt werden soll.
+ */
+ void setFlipH(bool flipH);
+
+ /**
+ @brief Legt fest, ob das Bild an der Y-Achse gespiegelt werden soll.
+ */
+ void setFlipV(bool flipV);
+
+ /**
+ @brief Gibt den aktuellen Alphawert des Bildes zurück.
+ @remark Diese Methode darf nur aufgerufen werden, wenn die Methode IsAlphaAllowed() true zurückgibt.
+ */
+ int getAlpha() {
+ return _modulationColor >> 24;
+ }
+
+ /**
+ @brief Gibt die aktuelle 24bit RGB Modulationsfarde des Bildes zurück.
+ @remark Diese Methode darf nur aufgerufen werden, wenn die Methode IsColorModulationAllowed() true zurückgibt.
+ */
+ int getModulationColor() {
+ return _modulationColor & 0x00ffffff;
+ }
+
+ /**
+ @brief Gibt den Skalierungsfakter des Bitmaps auf der X-Achse zurück.
+ @remark Diese Methode darf nur aufgerufen werden, wenn die Methode IsScalingAllowed() true zurückgibt.
+ */
+ float getScaleFactorX() const {
+ return _scaleFactorX;
+ }
+
+ /**
+ @brief Gibt den Skalierungsfakter des Bitmaps auf der Y-Achse zurück.
+ @remark Diese Methode darf nur aufgerufen werden, wenn die Methode IsScalingAllowed() true zurückgibt.
+ */
+ float getScaleFactorY() const {
+ return _scaleFactorY;
+ }
+
+ /**
+ @brief Gibt zurück, ob das Bild an der X-Achse gespiegelt angezeigt wird.
+ */
+ bool isFlipH() {
+ return _flipH;
+ }
+
+ /**
+ @brief Gibt zurück, ob das Bild an der Y-Achse gespiegelt angezeigt wird.
+ */
+ bool isFlipV() {
+ return _flipV;
+ }
+
+ // -----------------------------------------------------------------------------
+ // Die folgenden Methoden müssen alle BS_Bitmap-Klassen implementieren
+ // -----------------------------------------------------------------------------
+
+ /**
+ @brief Liest einen Pixel des Bildes.
+ @param X die X-Koordinate des Pixels.
+ @param Y die Y-Koordinate des Pixels
+ @return Gibt den 32-Bit Farbwert des Pixels an der übergebenen Koordinate zurück.
+ @remark Diese Methode sollte auf keine Fall benutzt werden um größere Teile des Bildes zu lesen, da sie sehr langsam ist. Sie ist
+ eher dafür gedacht einzelne Pixel des Bildes auszulesen.
+ */
+ virtual uint getPixel(int x, int y) const = 0;
+
+ /**
+ @brief Füllt den Inhalt des Bildes mit Pixeldaten.
+ @param Pixeldata ein Vector der die Pixeldaten enthält. Sie müssen in dem Farbformat des Bildes vorliegen und es müssen genügend Daten
+ vorhanden sein, um das ganze Bild zu füllen.
+ @param Offset der Offset in Byte im Pixeldata-Vector an dem sich der erste zu schreibende Pixel befindet.<br>
+ Der Standardwert ist 0.
+ @param Stride der Abstand in Byte zwischen dem Zeilenende und dem Beginn einer neuen Zeile im Pixeldata-Vector.<br>
+ Der Standardwert ist 0.
+ @return Gibt false zurück, falls der Aufruf fehlgeschlagen ist.
+ @remark Ein Aufruf dieser Methode ist nur erlaubt, wenn IsSetContentAllowed() true zurückgibt.
+ */
+ virtual bool setContent(const byte *pixeldata, uint size, uint offset = 0, uint stride = 0) = 0;
+
+ virtual bool isScalingAllowed() const = 0;
+ virtual bool isAlphaAllowed() const = 0;
+ virtual bool isColorModulationAllowed() const = 0;
+ virtual bool isSetContentAllowed() const = 0;
+
+ virtual bool persist(OutputPersistenceBlock &writer);
+ virtual bool unpersist(InputPersistenceBlock &reader);
+
+protected:
+ bool _flipH;
+ bool _flipV;
+ float _scaleFactorX;
+ float _scaleFactorY;
+ uint _modulationColor;
+ int _originalWidth;
+ int _originalHeight;
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/gfx/bitmapresource.cpp b/engines/sword25/gfx/bitmapresource.cpp
new file mode 100644
index 0000000000..cf3c85e3ac
--- /dev/null
+++ b/engines/sword25/gfx/bitmapresource.cpp
@@ -0,0 +1,62 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#include "sword25/gfx/bitmapresource.h"
+#include "sword25/kernel/kernel.h"
+#include "sword25/gfx/graphicengine.h"
+#include "sword25/package/packagemanager.h"
+
+namespace Sword25 {
+
+#define BS_LOG_PREFIX "BITMAP"
+
+BitmapResource::BitmapResource(const Common::String &filename, Image *pImage) :
+ _valid(false),
+ _pImage(pImage),
+ Resource(filename, Resource::TYPE_BITMAP) {
+ _valid = _pImage != 0;
+}
+
+BitmapResource::~BitmapResource() {
+ delete _pImage;
+}
+
+uint BitmapResource::getPixel(int x, int y) const {
+ BS_ASSERT(x >= 0 && x < _pImage->getWidth());
+ BS_ASSERT(y >= 0 && y < _pImage->getHeight());
+
+ return _pImage->getPixel(x, y);
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/gfx/bitmapresource.h b/engines/sword25/gfx/bitmapresource.h
new file mode 100644
index 0000000000..37849f918e
--- /dev/null
+++ b/engines/sword25/gfx/bitmapresource.h
@@ -0,0 +1,212 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#ifndef SWORD25_BITMAP_RESOURCE_H
+#define SWORD25_BITMAP_RESOURCE_H
+
+#include "sword25/kernel/common.h"
+#include "sword25/kernel/resource.h"
+#include "sword25/gfx/image/image.h"
+
+namespace Sword25 {
+
+class BitmapResource : public Resource {
+public:
+ /**
+ @brief Die möglichen Flippingparameter für die Blit-Methode.
+ */
+ enum FLIP_FLAGS {
+ /// Das Bild wird nicht gespiegelt.
+ FLIP_NONE = 0,
+ /// Das Bild wird an der horizontalen Achse gespiegelt.
+ FLIP_H = 1,
+ /// Das Bild wird an der vertikalen Achse gespiegelt.
+ FLIP_V = 2,
+ /// Das Bild wird an der horizontalen und vertikalen Achse gespiegelt.
+ FLIP_HV = FLIP_H | FLIP_V,
+ /// Das Bild wird an der horizontalen und vertikalen Achse gespiegelt.
+ FLIP_VH = FLIP_H | FLIP_V
+ };
+
+ BitmapResource(const Common::String &filename, Image *pImage);
+ virtual ~BitmapResource();
+
+ /**
+ @brief Gibt zurück, ob das Objekt einen gültigen Zustand hat.
+ */
+ bool isValid() const {
+ return _valid;
+ }
+
+ /**
+ @brief Gibt die Breite des Bitmaps zurück.
+ */
+ int getWidth() const {
+ BS_ASSERT(_pImage);
+ return _pImage->getWidth();
+ }
+
+ /**
+ @brief Gibt die Höhe des Bitmaps zurück.
+ */
+ int getHeight() const {
+ BS_ASSERT(_pImage);
+ return _pImage->getHeight();
+ }
+
+ /**
+ @brief Rendert das Bild in den Framebuffer.
+ @param PosX die Position auf der X-Achse im Zielbild in Pixeln, an der das Bild gerendert werden soll.<br>
+ Der Standardwert ist 0.
+ @param PosY die Position auf der Y-Achse im Zielbild in Pixeln, an der das Bild gerendert werden soll.<br>
+ Der Standardwert ist 0.
+ @param Flipping gibt an, wie das Bild gespiegelt werden soll.<br>
+ Der Standardwert ist BS_Image::FLIP_NONE (keine Spiegelung)
+ @param pSrcPartRect Pointer auf ein Common::Rect, welches den Ausschnitt des Quellbildes spezifiziert, der gerendert
+ werden soll oder NULL, falls das gesamte Bild gerendert werden soll.<br>
+ Dieser Ausschnitt bezieht sich auf das ungespiegelte und unskalierte Bild.<br>
+ Der Standardwert ist NULL.
+ @param Color ein ARGB Farbwert, der die Parameter für die Farbmodulation und fürs Alphablending festlegt.<br>
+ Die Alpha-Komponente der Farbe bestimmt den Alphablending Parameter (0 = keine Deckung, 255 = volle Deckung).<br>
+ Die Farbkomponenten geben die Farbe für die Farbmodulation an.<br>
+ Der Standardwert is BS_ARGB(255, 255, 255, 255) (volle Deckung, keine Farbmodulation).
+ Zum Erzeugen des Farbwertes können die Makros BS_RGB und BS_ARGB benutzt werden.
+ @param Width gibt die Ausgabebreite des Bildausschnittes an.
+ Falls diese von der Breite des Bildausschnittes abweicht wird
+ das Bild entsprechend Skaliert.<br>
+ Der Wert -1 gibt an, dass das Bild nicht Skaliert werden soll.<br>
+ Der Standardwert ist -1.
+ @param Width gibt die Ausgabehöhe des Bildausschnittes an.
+ Falls diese von der Höhe des Bildauschnittes abweicht, wird
+ das Bild entsprechend Skaliert.<br>
+ Der Wert -1 gibt an, dass das Bild nicht Skaliert werden soll.<br>
+ Der Standardwert ist -1.
+ @return Gibt false zurück, falls das Rendern fehlgeschlagen ist.
+ @remark Er werden nicht alle Blitting-Operationen von allen BS_Image-Klassen unterstützt.<br>
+ Mehr Informationen gibt es in der Klassenbeschreibung von BS_Image und durch folgende Methoden:
+ - IsBlitTarget()
+ - IsScalingAllowed()
+ - IsFillingAllowed()
+ - IsAlphaAllowed()
+ - IsColorModulationAllowed()
+ */
+ bool blit(int posX = 0, int posY = 0,
+ int flipping = FLIP_NONE,
+ Common::Rect *pSrcPartRect = NULL,
+ uint color = BS_ARGB(255, 255, 255, 255),
+ int width = -1, int height = -1) {
+ BS_ASSERT(_pImage);
+ return _pImage->blit(posX, posY, flipping, pSrcPartRect, color, width, height);
+ }
+
+ /**
+ @brief Füllt einen Rechteckigen Bereich des Bildes mit einer Farbe.
+ @param pFillRect Pointer auf ein Common::Rect, welches den Ausschnitt des Bildes spezifiziert, der gefüllt
+ werden soll oder NULL, falls das gesamte Bild gefüllt werden soll.<br>
+ Der Standardwert ist NULL.
+ @param Color der 32 Bit Farbwert mit dem der Bildbereich gefüllt werden soll.
+ @remark Ein Aufruf dieser Methode ist nur gestattet, wenn IsFillingAllowed() true zurückgibt.
+ @remark Es ist möglich über die Methode transparente Rechtecke darzustellen, indem man eine Farbe mit einem Alphawert ungleich
+ 255 angibt.
+ @remark Unabhängig vom Farbformat des Bildes muss ein 32 Bit Farbwert angegeben werden. Zur Erzeugung, können die Makros
+ BS_RGB und BS_ARGB benutzt werden.
+ @remark Falls das Rechteck nicht völlig innerhalb des Bildschirms ist, wird es automatisch zurechtgestutzt.
+ */
+ bool fill(const Common::Rect *pFillRect = 0, uint color = BS_RGB(0, 0, 0)) {
+ BS_ASSERT(_pImage);
+ return _pImage->fill(pFillRect, color);
+ }
+
+ /**
+ @brief Liest einen Pixel des Bildes.
+ @param X die X-Koordinate des Pixels.
+ @param Y die Y-Koordinate des Pixels
+ @return Gibt den 32-Bit Farbwert des Pixels an der übergebenen Koordinate zurück.
+ @remark Diese Methode sollte auf keine Fall benutzt werden um größere Teile des Bildes zu lesen, da sie sehr langsam ist. Sie ist
+ eher dafür gedacht einzelne Pixel des Bildes auszulesen.
+ */
+ uint getPixel(int x, int y) const;
+
+ //@{
+ /** @name Auskunfts-Methoden */
+
+ /**
+ @brief Überprüft, ob das BS_Image ein Zielbild für einen Blit-Aufruf sein kann.
+ @return Gibt false zurück, falls ein Blit-Aufruf mit diesem Objekt als Ziel nicht gestattet ist.
+ */
+ bool isBlitTarget() {
+ BS_ASSERT(_pImage);
+ return _pImage->isBlitTarget();
+ }
+
+ /**
+ @brief Gibt true zurück, falls das BS_Image bei einem Aufruf von Blit() skaliert dargestellt werden kann.
+ */
+ bool isScalingAllowed() {
+ BS_ASSERT(_pImage);
+ return _pImage->isScalingAllowed();
+ }
+
+ /**
+ @brief Gibt true zurück, wenn das BS_Image mit einem Aufruf von Fill() gefüllt werden kann.
+ */
+ bool isFillingAllowed() {
+ BS_ASSERT(_pImage);
+ return _pImage->isFillingAllowed();
+ }
+
+ /**
+ @brief Gibt true zurück, wenn das BS_Image bei einem Aufruf von Blit() mit einem Alphawert dargestellt werden kann.
+ */
+ bool isAlphaAllowed() {
+ BS_ASSERT(_pImage);
+ return _pImage->isAlphaAllowed();
+ }
+
+ /**
+ @brief Gibt true zurück, wenn das BS_Image bei einem Aufruf von Blit() mit Farbmodulation dargestellt werden kann.
+ */
+ bool isColorModulationAllowed() {
+ BS_ASSERT(_pImage);
+ return _pImage->isColorModulationAllowed();
+ }
+
+private:
+ Image *_pImage;
+ bool _valid;
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/gfx/dynamicbitmap.cpp b/engines/sword25/gfx/dynamicbitmap.cpp
new file mode 100644
index 0000000000..612e370712
--- /dev/null
+++ b/engines/sword25/gfx/dynamicbitmap.cpp
@@ -0,0 +1,155 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#include "sword25/gfx/dynamicbitmap.h"
+#include "sword25/gfx/bitmapresource.h"
+#include "sword25/package/packagemanager.h"
+#include "sword25/kernel/inputpersistenceblock.h"
+
+namespace Sword25 {
+
+#define BS_LOG_PREFIX "DYNAMICBITMAP"
+
+DynamicBitmap::DynamicBitmap(RenderObjectPtr<RenderObject> parentPtr, uint width, uint height) :
+ Bitmap(parentPtr, TYPE_DYNAMICBITMAP) {
+ // Das BS_Bitmap konnte nicht erzeugt werden, daher muss an dieser Stelle abgebrochen werden.
+ if (!_initSuccess) return;
+
+ _initSuccess = createRenderedImage(width, height);
+}
+
+DynamicBitmap::DynamicBitmap(InputPersistenceBlock &reader, RenderObjectPtr<RenderObject> parentPtr, uint handle) :
+ Bitmap(parentPtr, TYPE_DYNAMICBITMAP, handle) {
+ _initSuccess = unpersist(reader);
+}
+
+bool DynamicBitmap::createRenderedImage(uint width, uint height) {
+ // RenderedImage mit den gewünschten Maßen erstellen
+ bool result = false;
+ _image.reset(new RenderedImage(width, height, result));
+
+ _originalWidth = _width = width;
+ _originalHeight = _height = height;
+
+ return result;
+}
+
+DynamicBitmap::~DynamicBitmap() {
+}
+
+uint DynamicBitmap::getPixel(int x, int y) const {
+ BS_ASSERT(x >= 0 && x < _width);
+ BS_ASSERT(y >= 0 && y < _height);
+
+ return _image->getPixel(x, y);
+}
+
+bool DynamicBitmap::doRender() {
+ // Framebufferobjekt holen
+ GraphicEngine *pGfx = Kernel::getInstance()->getGfx();
+ BS_ASSERT(pGfx);
+
+ // Bitmap zeichnen
+ bool result;
+ if (_scaleFactorX == 1.0f && _scaleFactorY == 1.0f) {
+ result = _image->blit(_absoluteX, _absoluteY,
+ (_flipV ? BitmapResource::FLIP_V : 0) |
+ (_flipH ? BitmapResource::FLIP_H : 0),
+ 0, _modulationColor, -1, -1);
+ } else {
+ result = _image->blit(_absoluteX, _absoluteY,
+ (_flipV ? BitmapResource::FLIP_V : 0) |
+ (_flipH ? BitmapResource::FLIP_H : 0),
+ 0, _modulationColor, _width, _height);
+ }
+
+ return result;
+}
+
+bool DynamicBitmap::setContent(const byte *pixeldata, uint size, uint offset, uint stride) {
+ return _image->setContent(pixeldata, size, offset, stride);
+}
+
+bool DynamicBitmap::isScalingAllowed() const {
+ return _image->isScalingAllowed();
+}
+
+bool DynamicBitmap::isAlphaAllowed() const {
+ return _image->isAlphaAllowed();
+}
+
+bool DynamicBitmap::isColorModulationAllowed() const {
+ return _image->isColorModulationAllowed();
+}
+
+bool DynamicBitmap::isSetContentAllowed() const {
+ return true;
+}
+
+bool DynamicBitmap::persist(OutputPersistenceBlock &writer) {
+ bool result = true;
+
+ result &= Bitmap::persist(writer);
+
+ // Bilddaten werden nicht gespeichert. Dies ist auch nicht weiter von bedeutung, da BS_DynamicBitmap nur vom Videoplayer benutzt wird.
+ // Während ein Video abläuft kann niemals gespeichert werden. BS_DynamicBitmap kann nur der Vollständigkeit halber persistiert werden.
+ BS_LOG_WARNINGLN("Persisting a BS_DynamicBitmap. Bitmap content is not persisted.");
+
+ result &= RenderObject::persistChildren(writer);
+
+ return result;
+}
+
+bool DynamicBitmap::unpersist(InputPersistenceBlock &reader) {
+ bool result = true;
+
+ result &= Bitmap::unpersist(reader);
+
+ // Ein RenderedImage mit den gespeicherten Maßen erstellen.
+ result &= createRenderedImage(_width, _height);
+
+ // Bilddaten werden nicht gespeichert (s.o.).
+ BS_LOG_WARNINGLN("Unpersisting a BS_DynamicBitmap. Bitmap contents are missing.");
+
+ // Bild mit durchsichtigen Bilddaten initialisieren.
+ byte *transparentImageData = (byte *)calloc(_width * _height * 4, 1);
+ _image->setContent(transparentImageData, _width * _height);
+ free(transparentImageData);
+
+ result &= RenderObject::unpersistChildren(reader);
+
+ return reader.isGood() && result;
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/gfx/dynamicbitmap.h b/engines/sword25/gfx/dynamicbitmap.h
new file mode 100644
index 0000000000..1737bdf5fc
--- /dev/null
+++ b/engines/sword25/gfx/dynamicbitmap.h
@@ -0,0 +1,78 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#ifndef SWORD25_DYNAMIC_BITMAP_H
+#define SWORD25_DYNAMIC_BITMAP_H
+
+#include "sword25/kernel/common.h"
+#include "sword25/gfx/bitmap.h"
+#include "sword25/gfx/image/renderedimage.h"
+
+#include "common/ptr.h"
+
+namespace Sword25 {
+
+class DynamicBitmap : public Bitmap {
+ friend class RenderObject;
+
+public:
+ virtual ~DynamicBitmap();
+
+ virtual uint getPixel(int x, int y) const;
+
+ virtual bool setContent(const byte *pixeldata, uint size, uint offset, uint stride);
+
+ virtual bool isScalingAllowed() const;
+ virtual bool isAlphaAllowed() const;
+ virtual bool isColorModulationAllowed() const;
+ virtual bool isSetContentAllowed() const;
+
+ virtual bool persist(OutputPersistenceBlock &writer);
+ virtual bool unpersist(InputPersistenceBlock &reader);
+
+protected:
+ virtual bool doRender();
+
+private:
+ DynamicBitmap(RenderObjectPtr<RenderObject> parentPtr, uint width, uint height);
+ DynamicBitmap(InputPersistenceBlock &reader, RenderObjectPtr<RenderObject> parentPtr, uint handle);
+
+ bool createRenderedImage(uint width, uint height);
+
+ Common::ScopedPtr<RenderedImage> _image;
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/gfx/fontresource.cpp b/engines/sword25/gfx/fontresource.cpp
new file mode 100644
index 0000000000..dbb9c67fe5
--- /dev/null
+++ b/engines/sword25/gfx/fontresource.cpp
@@ -0,0 +1,138 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#define BS_LOG_PREFIX "FONTRESOURCE"
+
+#include "sword25/kernel/kernel.h"
+#include "sword25/package/packagemanager.h"
+
+#include "sword25/gfx/fontresource.h"
+
+namespace Sword25 {
+
+enum {
+ DEFAULT_LINEHEIGHT = 20,
+ DEFAULT_GAPWIDTH = 1
+};
+
+FontResource::FontResource(Kernel *pKernel, const Common::String &fileName) :
+ _pKernel(pKernel),
+ _valid(false),
+ Resource(fileName, Resource::TYPE_FONT),
+ Common::XMLParser() {
+
+ // Get a pointer to the package manager
+ BS_ASSERT(_pKernel);
+ PackageManager *pPackage = _pKernel->getPackage();
+ BS_ASSERT(pPackage);
+
+ // Load the contents of the file
+ uint fileSize;
+ char *xmlData = pPackage->getXmlFile(getFileName(), &fileSize);
+ if (!xmlData) {
+ BS_LOG_ERRORLN("Could not read \"%s\".", getFileName().c_str());
+ return;
+ }
+
+ // Parse the contents
+ if (!loadBuffer((const byte *)xmlData, fileSize))
+ return;
+
+ _valid = parse();
+ close();
+ free(xmlData);
+}
+
+bool FontResource::parserCallback_font(ParserNode *node) {
+ // Get the attributes of the font
+ Common::String bitmapFilename = node->values["bitmap"];
+
+ if (!parseIntegerKey(node->values["lineheight"], 1, &_lineHeight)) {
+ BS_LOG_WARNINGLN("Illegal or missing lineheight attribute in <font> tag in \"%s\". Assuming default (\"%d\").",
+ getFileName().c_str(), DEFAULT_LINEHEIGHT);
+ _lineHeight = DEFAULT_LINEHEIGHT;
+ }
+
+ if (!parseIntegerKey(node->values["gap"], 1, &_gapWidth)) {
+ BS_LOG_WARNINGLN("Illegal or missing gap attribute in <font> tag in \"%s\". Assuming default (\"%d\").",
+ getFileName().c_str(), DEFAULT_GAPWIDTH);
+ _gapWidth = DEFAULT_GAPWIDTH;
+ }
+
+ // Get a reference to the package manager
+ BS_ASSERT(_pKernel);
+ PackageManager *pPackage = _pKernel->getPackage();
+ BS_ASSERT(pPackage);
+
+ // Get the full path and filename for the bitmap resource
+ _bitmapFileName = pPackage->getAbsolutePath(bitmapFilename);
+ if (_bitmapFileName == "") {
+ BS_LOG_ERRORLN("Image file \"%s\" was specified in <font> tag of \"%s\" but could not be found.",
+ _bitmapFileName.c_str(), getFileName().c_str());
+ }
+
+ // Pre-cache the resource
+ if (!_pKernel->getResourceManager()->precacheResource(_bitmapFileName)) {
+ BS_LOG_ERRORLN("Could not precache \"%s\".", _bitmapFileName.c_str());
+ }
+
+ return true;
+}
+
+bool FontResource::parserCallback_character(ParserNode *node) {
+ // Get the attributes of the character
+ int charCode, top, left, right, bottom;
+
+ if (!parseIntegerKey(node->values["code"], 1, &charCode) || (charCode < 0) || (charCode >= 256)) {
+ return parserError("Illegal or missing code attribute in <character> tag in \"%s\".", getFileName().c_str());
+ }
+
+ if (!parseIntegerKey(node->values["top"], 1, &top) || (top < 0)) {
+ return parserError("Illegal or missing top attribute in <character> tag in \"%s\".", getFileName().c_str());
+ }
+ if (!parseIntegerKey(node->values["left"], 1, &left) || (left < 0)) {
+ return parserError("Illegal or missing left attribute in <character> tag in \"%s\".", getFileName().c_str());
+ }
+ if (!parseIntegerKey(node->values["right"], 1, &right) || (right < 0)) {
+ return parserError("Illegal or missing right attribute in <character> tag in \"%s\".", getFileName().c_str());
+ }
+ if (!parseIntegerKey(node->values["bottom"], 1, &bottom) || (bottom < 0)) {
+ return parserError("Illegal or missing bottom attribute in <character> tag in \"%s\".", getFileName().c_str());
+ }
+
+ this->_characterRects[charCode] = Common::Rect(left, top, right, bottom);
+ return true;
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/gfx/fontresource.h b/engines/sword25/gfx/fontresource.h
new file mode 100644
index 0000000000..19c44d0ade
--- /dev/null
+++ b/engines/sword25/gfx/fontresource.h
@@ -0,0 +1,134 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#ifndef SWORD25_FONTRESOURCE_H
+#define SWORD25_FONTRESOURCE_H
+
+#include "common/scummsys.h"
+#include "common/rect.h"
+#include "common/xmlparser.h"
+#include "sword25/kernel/common.h"
+#include "sword25/kernel/resource.h"
+
+namespace Sword25 {
+
+class Kernel;
+
+class FontResource : public Resource, Common::XMLParser {
+public:
+ /**
+ @brief Erzeugt eine neues Exemplar von BS_FontResource
+ @param pKernel ein Pointer auf den Kernel
+ @param FileName der Dateiname der zu ladenen Resource
+ @remark Wenn der Konstruktor erfolgreich ausgeführt werden konnte gibt die Methode IsValid true zurück.
+ */
+ FontResource(Kernel *pKernel, const Common::String &fileName);
+
+ /**
+ @brief Gibt true zurück, wenn das Objekt korrekt initialisiert wurde.
+
+ Diese Methode kann dazu benutzt werden um festzustellen, ob der Konstruktor erfolgreich ausgeführt wurde.
+ */
+ bool isValid() const {
+ return _valid;
+ }
+
+ /**
+ @brief Gibt die Zeilenhöhe des Fonts in Pixeln zurück.
+
+ Die Zeilenhöhe ist der Wert, der zur Y-Koordinate addiert wird, wenn ein Zeilenumbruch auftritt.
+ */
+ int getLineHeight() const {
+ return _lineHeight;
+ }
+
+ /**
+ @brief Gibt den Buchstabenabstand der Fonts in Pixeln zurück.
+
+ Der Buchstabenabstand ist der Wert, der zwischen zwei Buchstaben freigelassen wird.
+ */
+ int getGapWidth() const {
+ return _gapWidth;
+ }
+
+ /**
+ @brief Gibt das Bounding-Rect eines Zeichens auf der Charactermap zurück.
+ @param Character der ASCII-Code des Zeichens
+ @return Das Bounding-Rect des übergebenen Zeichens auf der Charactermap.
+ */
+ const Common::Rect &getCharacterRect(int character) const {
+ BS_ASSERT(character >= 0 && character < 256);
+ return _characterRects[character];
+ }
+
+ /**
+ @brief Gibt den Dateinamen der Charactermap zurück.
+ */
+ const Common::String &getCharactermapFileName() const {
+ return _bitmapFileName;
+ }
+
+private:
+ Kernel *_pKernel;
+ bool _valid;
+ Common::String _bitmapFileName;
+ int _lineHeight;
+ int _gapWidth;
+ Common::Rect _characterRects[256];
+
+ // Parser
+ CUSTOM_XML_PARSER(FontResource) {
+ XML_KEY(font)
+ XML_PROP(bitmap, true)
+ XML_PROP(lineheight, false)
+ XML_PROP(gap, false)
+
+ XML_KEY(character)
+ XML_PROP(code, true)
+ XML_PROP(left, true)
+ XML_PROP(top, true)
+ XML_PROP(right, true)
+ XML_PROP(bottom, true)
+ KEY_END()
+ KEY_END()
+ } PARSER_END()
+
+ // Parser callback methods
+ bool parserCallback_font(ParserNode *node);
+ bool parserCallback_character(ParserNode *node);
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/gfx/framecounter.cpp b/engines/sword25/gfx/framecounter.cpp
new file mode 100644
index 0000000000..07415cc2dc
--- /dev/null
+++ b/engines/sword25/gfx/framecounter.cpp
@@ -0,0 +1,68 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#include "common/system.h"
+#include "sword25/gfx/framecounter.h"
+
+namespace Sword25 {
+
+Framecounter::Framecounter(int updateFrequency) :
+ _FPS(0),
+ _FPSCount(0),
+ _lastUpdateTime(-1) {
+ setUpdateFrequency(updateFrequency);
+}
+
+void Framecounter::update() {
+ // Aktuellen Systemtimerstand auslesen
+ uint64 timer = g_system->getMillis() * 1000;
+
+ // Falls m_LastUpdateTime == -1 ist, wird der Frame-Counter zum ersten Mal aufgerufen und der aktuelle Systemtimer als erster
+ // Messzeitpunkt genommen.
+ if (_lastUpdateTime == -1)
+ _lastUpdateTime = timer;
+ else {
+ // Die Anzahl der Frames im aktuellen Messzeitraum wird erhöht.
+ _FPSCount++;
+
+ // Falls der Messzeitraum verstrichen ist, wird die durchschnittliche Framerate berechnet und ein neuer Messzeitraum begonnen.
+ if (timer - _lastUpdateTime >= _updateDelay) {
+ _FPS = static_cast<int>((1000000 * (uint64)_FPSCount) / (timer - _lastUpdateTime));
+ _lastUpdateTime = timer;
+ _FPSCount = 0;
+ }
+ }
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/gfx/framecounter.h b/engines/sword25/gfx/framecounter.h
new file mode 100644
index 0000000000..994950573f
--- /dev/null
+++ b/engines/sword25/gfx/framecounter.h
@@ -0,0 +1,99 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#ifndef SWORD25_FRAMECOUNTER_H
+#define SWORD25_FRAMECOUNTER_H
+
+// Includes
+#include "sword25/kernel/common.h"
+
+namespace Sword25 {
+
+/**
+ * A simple class that implements a frame counter
+ */
+class Framecounter {
+private:
+
+ // TODO: This class should be rewritten based on Audio::Timestamp,
+ // which provides higher accuracy and avoids using 64 bit data types.
+ typedef unsigned long long uint64;
+ typedef signed long long int64;
+
+ enum {
+ DEFAULT_UPDATE_FREQUENCY = 10
+ };
+
+public:
+ /**
+ * Creates a new BS_Framecounter object
+ * @param UpdateFrequency Specifies how often the frame counter should be updated in a sceond.
+ * The default value is 10.
+ */
+ Framecounter(int updateFrequency = DEFAULT_UPDATE_FREQUENCY);
+
+ /**
+ * Determines how often the frame counter should be updated in a second.
+ * @param UpdateFrequency Specifies how often the frame counter should be updated in a second.
+ */
+ inline void setUpdateFrequency(int updateFrequency);
+
+ /**
+ * This method must be called once per frame.
+ */
+ void update();
+
+ /**
+ * Returns the current FPS value.
+ */
+ int getFPS() const {
+ return _FPS;
+ }
+
+private:
+ int _FPS;
+ int _FPSCount;
+ int64 _lastUpdateTime;
+ uint64 _updateDelay;
+};
+
+// Inlines
+void Framecounter::setUpdateFrequency(int updateFrequency) {
+ // Frequency in time (converted to microseconds)
+ _updateDelay = 1000000 / updateFrequency;
+}
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/gfx/graphicengine.cpp b/engines/sword25/gfx/graphicengine.cpp
new file mode 100644
index 0000000000..f629993abf
--- /dev/null
+++ b/engines/sword25/gfx/graphicengine.cpp
@@ -0,0 +1,494 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#define BS_LOG_PREFIX "GRAPHICENGINE"
+
+#include "common/system.h"
+
+#include "sword25/gfx/bitmapresource.h"
+#include "sword25/gfx/animationresource.h"
+#include "sword25/gfx/fontresource.h"
+#include "sword25/gfx/panel.h"
+#include "sword25/gfx/renderobjectmanager.h"
+#include "sword25/gfx/screenshot.h"
+#include "sword25/gfx/image/renderedimage.h"
+#include "sword25/gfx/image/swimage.h"
+#include "sword25/gfx/image/vectorimage.h"
+#include "sword25/package/packagemanager.h"
+#include "sword25/kernel/inputpersistenceblock.h"
+#include "sword25/kernel/outputpersistenceblock.h"
+
+
+#include "sword25/gfx/graphicengine.h"
+
+#include "sword25/util/lua/lua.h"
+#include "sword25/util/lua/lauxlib.h"
+enum {
+ BIT_DEPTH = 32,
+ BACKBUFFER_COUNT = 1
+};
+
+
+namespace Sword25 {
+
+static const uint FRAMETIME_SAMPLE_COUNT = 5; // Anzahl der Framezeiten über die, die Framezeit gemittelt wird
+
+GraphicEngine::GraphicEngine(Kernel *pKernel) :
+ _width(0),
+ _height(0),
+ _bitDepth(0),
+ _windowed(0),
+ _lastTimeStamp((uint) -1), // max. BS_INT64 um beim ersten Aufruf von _UpdateLastFrameDuration() einen Reset zu erzwingen
+ _lastFrameDuration(0),
+ _timerActive(true),
+ _frameTimeSampleSlot(0),
+ _repaintedPixels(0),
+ _thumbnail(NULL),
+ ResourceService(pKernel) {
+ _frameTimeSamples.resize(FRAMETIME_SAMPLE_COUNT);
+
+ if (!registerScriptBindings())
+ BS_LOG_ERRORLN("Script bindings could not be registered.");
+ else
+ BS_LOGLN("Script bindings registered.");
+}
+
+GraphicEngine::~GraphicEngine() {
+ unregisterScriptBindings();
+ _backSurface.free();
+ _frameBuffer.free();
+ delete _thumbnail;
+}
+
+bool GraphicEngine::init(int width, int height, int bitDepth, int backbufferCount, bool isWindowed_) {
+ // Warnung ausgeben, wenn eine nicht unterstützte Bittiefe gewählt wurde.
+ if (bitDepth != BIT_DEPTH) {
+ BS_LOG_WARNINGLN("Can't use a bit depth of %d (not supported). Falling back to %d.", bitDepth, BIT_DEPTH);
+ _bitDepth = BIT_DEPTH;
+ }
+
+ // Warnung ausgeben, wenn nicht genau ein Backbuffer gewählt wurde.
+ if (backbufferCount != BACKBUFFER_COUNT) {
+ BS_LOG_WARNINGLN("Can't use %d backbuffers (not supported). Falling back to %d.", backbufferCount, BACKBUFFER_COUNT);
+ backbufferCount = BACKBUFFER_COUNT;
+ }
+
+ // Parameter in lokale Variablen kopieren
+ _width = width;
+ _height = height;
+ _bitDepth = bitDepth;
+ _windowed = isWindowed_;
+ _screenRect.left = 0;
+ _screenRect.top = 0;
+ _screenRect.right = _width;
+ _screenRect.bottom = _height;
+
+ _backSurface.create(width, height, 4);
+ _frameBuffer.create(width, height, 4);
+
+ // Standardmäßig ist Vsync an.
+ setVsync(true);
+
+ // Layer-Manager initialisieren.
+ _renderObjectManagerPtr.reset(new RenderObjectManager(width, height, backbufferCount + 1));
+
+ // Hauptpanel erstellen
+ _mainPanelPtr = _renderObjectManagerPtr->getTreeRoot()->addPanel(width, height, BS_ARGB(0, 0, 0, 0));
+ if (!_mainPanelPtr.isValid())
+ return false;
+ _mainPanelPtr->setVisible(true);
+
+ return true;
+}
+
+bool GraphicEngine::startFrame(bool updateAll) {
+ // Berechnen, wie viel Zeit seit dem letzten Frame vergangen ist.
+ // Dieser Wert kann über GetLastFrameDuration() von Modulen abgefragt werden, die zeitabhängig arbeiten.
+ updateLastFrameDuration();
+
+ // Den Layer-Manager auf den nächsten Frame vorbereiten
+ _renderObjectManagerPtr->startFrame();
+
+ return true;
+}
+
+bool GraphicEngine::endFrame() {
+ // Scene zeichnen
+ _renderObjectManagerPtr->render();
+
+ // FIXME: The frame buffer surface is only used as the base for creating thumbnails when saving the
+ // game, since the _backSurface is blanked. Currently I'm doing a slightly hacky check and only
+ // copying the back surface if line 50 (the first line after the interface area) is non-blank
+ if (READ_LE_UINT32((byte *)_backSurface.pixels + (_backSurface.pitch * 50)) & 0xffffff) {
+ // Make a copy of the current frame into the frame buffer
+ Common::copy((byte *)_backSurface.pixels, (byte *)_backSurface.pixels +
+ (_backSurface.pitch * _backSurface.h), (byte *)_frameBuffer.pixels);
+ }
+
+ g_system->updateScreen();
+
+ // Debug-Lines zeichnen
+ if (!_debugLines.empty()) {
+#if 0
+ glEnable(GL_LINE_SMOOTH);
+ glBegin(GL_LINES);
+
+ Common::Array<DebugLine>::const_iterator iter = m_DebugLines.begin();
+ for (; iter != m_DebugLines.end(); ++iter) {
+ const uint &Color = (*iter).Color;
+ const BS_Vertex &Start = (*iter).Start;
+ const BS_Vertex &End = (*iter).End;
+
+ glColor4ub((Color >> 16) & 0xff, (Color >> 8) & 0xff, Color & 0xff, Color >> 24);
+ glVertex2d(Start.X, Start.Y);
+ glVertex2d(End.X, End.Y);
+ }
+
+ glEnd();
+ glDisable(GL_LINE_SMOOTH);
+#endif
+
+ warning("STUB: Drawing debug lines");
+
+ _debugLines.clear();
+ }
+
+ // Framecounter aktualisieren
+ _FPSCounter.update();
+
+ return true;
+}
+
+RenderObjectPtr<Panel> GraphicEngine::getMainPanel() {
+ return _mainPanelPtr;
+}
+
+void GraphicEngine::setVsync(bool vsync) {
+ warning("STUB: SetVsync(%d)", vsync);
+}
+
+bool GraphicEngine::getVsync() const {
+ warning("STUB: getVsync()");
+
+ return true;
+}
+
+bool GraphicEngine::fill(const Common::Rect *fillRectPtr, uint color) {
+ Common::Rect rect(_width - 1, _height - 1);
+
+ int ca = (color >> 24) & 0xff;
+
+ if (ca == 0)
+ return true;
+
+ int cr = (color >> 16) & 0xff;
+ int cg = (color >> 8) & 0xff;
+ int cb = (color >> 0) & 0xff;
+
+ if (fillRectPtr) {
+ rect = *fillRectPtr;
+ }
+
+ if (rect.width() > 0 && rect.height() > 0) {
+ if (ca == 0xff) {
+ _backSurface.fillRect(rect, color);
+ } else {
+ byte *outo = (byte *)_backSurface.getBasePtr(rect.left, rect.top);
+ byte *out;
+
+ for (int i = rect.top; i < rect.bottom; i++) {
+ out = outo;
+ for (int j = rect.left; j < rect.right; j++) {
+ *out += (byte)(((cb - *out) * ca) >> 8);
+ out++;
+ *out += (byte)(((cg - *out) * ca) >> 8);
+ out++;
+ *out += (byte)(((cr - *out) * ca) >> 8);
+ out++;
+ *out = 255;
+ out++;
+ }
+
+ outo += _backSurface.pitch;
+ }
+ }
+
+ g_system->copyRectToScreen((byte *)_backSurface.getBasePtr(rect.left, rect.top), _backSurface.pitch, rect.left, rect.top, rect.width(), rect.height());
+ }
+
+ return true;
+}
+
+Graphics::Surface *GraphicEngine::getScreenshot() {
+ return &_frameBuffer;
+}
+
+// -----------------------------------------------------------------------------
+// RESOURCE MANAGING
+// -----------------------------------------------------------------------------
+
+Resource *GraphicEngine::loadResource(const Common::String &filename) {
+ BS_ASSERT(canLoadResource(filename));
+
+ // Load image for "software buffer" (FIXME: Whatever that means?)
+ if (filename.hasSuffix("_s.png")) {
+ bool result = false;
+ SWImage *pImage = new SWImage(filename, result);
+ if (!result) {
+ delete pImage;
+ return 0;
+ }
+
+ BitmapResource *pResource = new BitmapResource(filename, pImage);
+ if (!pResource->isValid()) {
+ delete pResource;
+ return 0;
+ }
+
+ return pResource;
+ }
+
+ // Load sprite image
+ if (filename.hasSuffix(".png") || filename.hasSuffix(".b25s")) {
+ bool result = false;
+ RenderedImage *pImage = new RenderedImage(filename, result);
+ if (!result) {
+ delete pImage;
+ return 0;
+ }
+
+ BitmapResource *pResource = new BitmapResource(filename, pImage);
+ if (!pResource->isValid()) {
+ delete pResource;
+ return 0;
+ }
+
+ return pResource;
+ }
+
+
+ // Load vector graphics
+ if (filename.hasSuffix(".swf")) {
+ debug(2, "VectorImage: %s", filename.c_str());
+
+ // Pointer auf Package-Manager holen
+ PackageManager *pPackage = Kernel::getInstance()->getPackage();
+ BS_ASSERT(pPackage);
+
+ // Datei laden
+ byte *pFileData;
+ uint fileSize;
+ if (!(pFileData = static_cast<byte *>(pPackage->getFile(filename, &fileSize)))) {
+ BS_LOG_ERRORLN("File \"%s\" could not be loaded.", filename.c_str());
+ return 0;
+ }
+
+ bool result = false;
+ VectorImage *pImage = new VectorImage(pFileData, fileSize, result, filename);
+ if (!result) {
+ delete pImage;
+ delete[] pFileData;
+ return 0;
+ }
+
+ BitmapResource *pResource = new BitmapResource(filename, pImage);
+ if (!pResource->isValid()) {
+ delete pResource;
+ delete[] pFileData;
+ return 0;
+ }
+
+ delete[] pFileData;
+ return pResource;
+ }
+
+ // Load animation
+ if (filename.hasSuffix("_ani.xml")) {
+ AnimationResource *pResource = new AnimationResource(filename);
+ if (pResource->isValid())
+ return pResource;
+ else {
+ delete pResource;
+ return 0;
+ }
+ }
+
+ // Load font
+ if (filename.hasSuffix("_fnt.xml")) {
+ FontResource *pResource = new FontResource(Kernel::getInstance(), filename);
+ if (pResource->isValid())
+ return pResource;
+ else {
+ delete pResource;
+ return 0;
+ }
+ }
+
+ BS_LOG_ERRORLN("Service cannot load \"%s\".", filename.c_str());
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+bool GraphicEngine::canLoadResource(const Common::String &filename) {
+ return filename.hasSuffix(".png") ||
+ filename.hasSuffix("_ani.xml") ||
+ filename.hasSuffix("_fnt.xml") ||
+ filename.hasSuffix(".swf") ||
+ filename.hasSuffix(".b25s");
+}
+
+
+// -----------------------------------------------------------------------------
+// DEBUGGING
+// -----------------------------------------------------------------------------
+
+void GraphicEngine::drawDebugLine(const Vertex &start, const Vertex &end, uint color) {
+ _debugLines.push_back(DebugLine(start, end, color));
+}
+
+void GraphicEngine::updateLastFrameDuration() {
+ // Record current time
+ const uint currentTime = Kernel::getInstance()->getMilliTicks();
+
+ // Compute the elapsed time since the last frame and prevent too big ( > 250 msecs) time jumps.
+ // These can occur when loading save states, during debugging or due to hardware inaccuracies.
+ _frameTimeSamples[_frameTimeSampleSlot] = static_cast<uint>(currentTime - _lastTimeStamp);
+ if (_frameTimeSamples[_frameTimeSampleSlot] > 250000)
+ _frameTimeSamples[_frameTimeSampleSlot] = 250000;
+ _frameTimeSampleSlot = (_frameTimeSampleSlot + 1) % FRAMETIME_SAMPLE_COUNT;
+
+ // Compute the average frame duration over multiple frames to eliminate outliers.
+ Common::Array<uint>::const_iterator it = _frameTimeSamples.begin();
+ uint sum = *it;
+ for (it++; it != _frameTimeSamples.end(); it++)
+ sum += *it;
+ _lastFrameDuration = sum * 1000 / FRAMETIME_SAMPLE_COUNT;
+
+ // Update m_LastTimeStamp with the current frame's timestamp
+ _lastTimeStamp = currentTime;
+}
+
+bool GraphicEngine::saveThumbnailScreenshot(const Common::String &filename) {
+ // Note: In ScumMVM, rather than saivng the thumbnail to a file, we store it in memory
+ // until needed when creating savegame files
+ delete _thumbnail;
+ _thumbnail = Screenshot::createThumbnail(&_frameBuffer);
+ return true;
+}
+
+void GraphicEngine::ARGBColorToLuaColor(lua_State *L, uint color) {
+ lua_Number components[4] = {
+ (color >> 16) & 0xff, // Rot
+ (color >> 8) & 0xff, // Grün
+ color & 0xff, // Blau
+ color >> 24, // Alpha
+ };
+
+ lua_newtable(L);
+
+ for (uint i = 1; i <= 4; i++) {
+ lua_pushnumber(L, i);
+ lua_pushnumber(L, components[i - 1]);
+ lua_settable(L, -3);
+ }
+}
+
+uint GraphicEngine::luaColorToARGBColor(lua_State *L, int stackIndex) {
+#ifdef DEBUG
+ int __startStackDepth = lua_gettop(L);
+#endif
+
+ // Sicherstellen, dass wir wirklich eine Tabelle betrachten
+ luaL_checktype(L, stackIndex, LUA_TTABLE);
+ // Größe der Tabelle auslesen
+ uint n = luaL_getn(L, stackIndex);
+ // RGB oder RGBA Farben werden unterstützt und sonst keine
+ if (n != 3 && n != 4)
+ luaL_argcheck(L, 0, stackIndex, "at least 3 of the 4 color components have to be specified");
+
+ // Red color component reading
+ lua_rawgeti(L, stackIndex, 1);
+ uint red = static_cast<uint>(lua_tonumber(L, -1));
+ if (!lua_isnumber(L, -1) || red >= 256)
+ luaL_argcheck(L, 0, stackIndex, "red color component must be an integer between 0 and 255");
+ lua_pop(L, 1);
+
+ // Green color component reading
+ lua_rawgeti(L, stackIndex, 2);
+ uint green = static_cast<uint>(lua_tonumber(L, -1));
+ if (!lua_isnumber(L, -1) || green >= 256)
+ luaL_argcheck(L, 0, stackIndex, "green color component must be an integer between 0 and 255");
+ lua_pop(L, 1);
+
+ // Blue color component reading
+ lua_rawgeti(L, stackIndex, 3);
+ uint blue = static_cast<uint>(lua_tonumber(L, -1));
+ if (!lua_isnumber(L, -1) || blue >= 256)
+ luaL_argcheck(L, 0, stackIndex, "blue color component must be an integer between 0 and 255");
+ lua_pop(L, 1);
+
+ // Alpha color component reading
+ uint alpha = 0xff;
+ if (n == 4) {
+ lua_rawgeti(L, stackIndex, 4);
+ alpha = static_cast<uint>(lua_tonumber(L, -1));
+ if (!lua_isnumber(L, -1) || alpha >= 256)
+ luaL_argcheck(L, 0, stackIndex, "alpha color component must be an integer between 0 and 255");
+ lua_pop(L, 1);
+ }
+
+#ifdef DEBUG
+ BS_ASSERT(__startStackDepth == lua_gettop(L));
+#endif
+
+ return (alpha << 24) | (red << 16) | (green << 8) | blue;
+}
+
+bool GraphicEngine::persist(OutputPersistenceBlock &writer) {
+ writer.write(_timerActive);
+
+ bool result = _renderObjectManagerPtr->persist(writer);
+
+ return result;
+}
+
+bool GraphicEngine::unpersist(InputPersistenceBlock &reader) {
+ reader.read(_timerActive);
+ _renderObjectManagerPtr->unpersist(reader);
+
+ return reader.isGood();
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/gfx/graphicengine.h b/engines/sword25/gfx/graphicengine.h
new file mode 100644
index 0000000000..e1ae0d2d62
--- /dev/null
+++ b/engines/sword25/gfx/graphicengine.h
@@ -0,0 +1,392 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+/*
+ * GraphicEngine
+ * ----------------
+ * This the graphics engine interface.
+ *
+ * Autor: Malte Thiesen
+ */
+
+#ifndef SWORD25_GRAPHICENGINE_H
+#define SWORD25_GRAPHICENGINE_H
+
+// Includes
+#include "common/array.h"
+#include "common/rect.h"
+#include "common/ptr.h"
+#include "common/str.h"
+#include "graphics/surface.h"
+#include "sword25/kernel/common.h"
+#include "sword25/kernel/resservice.h"
+#include "sword25/kernel/persistable.h"
+#include "sword25/gfx/framecounter.h"
+#include "sword25/gfx/renderobjectptr.h"
+#include "sword25/math/vertex.h"
+
+namespace Sword25 {
+
+class Kernel;
+class Image;
+class Panel;
+class Screenshot;
+class RenderObjectManager;
+
+typedef uint BS_COLOR;
+
+#define BS_RGB(R,G,B) (0xFF000000 | ((R) << 16) | ((G) << 8) | (B))
+#define BS_ARGB(A,R,G,B) (((A) << 24) | ((R) << 16) | ((G) << 8) | (B))
+
+/**
+ * This is the graphics engine. Unlike the original code, this is not
+ * an interface that needs to be subclassed, but rather already contains
+ * all required functionality.
+ */
+class GraphicEngine : public ResourceService, public Persistable {
+public:
+ // Enums
+ // -----
+
+ // Colour formats
+ //
+ /**
+ * The colour format used by the engine
+ */
+ enum COLOR_FORMATS {
+ /// Undefined/unknown colour format
+ CF_UNKNOWN = 0,
+ /**
+ * 24-bit colour format (R8G8B8)
+ */
+ CF_RGB24,
+ /**
+ * 32-bit colour format (A8R8G8B8) (little endian)
+ */
+ CF_ARGB32,
+ /**
+ 32-bit colour format (A8B8G8R8) (little endian)
+ */
+ CF_ABGR32
+ };
+
+ // Constructor
+ // -----------
+ GraphicEngine(Kernel *pKernel);
+ ~GraphicEngine();
+
+ // Interface
+ // ---------
+
+ /**
+ * Initialises the graphics engine and sets the screen mode. Returns
+ * true if initialisation failed.
+ * @note This method should be called immediately after the
+ * initialisation of all services.
+ *
+ * @param Height The height of the output buffer in pixels. The default value is 600
+ * @param BitDepth The bit depth of the desired output buffer in bits. The default value is 16
+ * @param BackbufferCount The number of back buffers to be created. The default value is 2
+ * @param Windowed Indicates whether the engine is to run in windowed mode.
+ */
+ bool init(int width = 800, int height = 600, int bitDepth = 16, int backbufferCount = 2, bool windowed = false);
+
+ /**
+ * Begins rendering a new frame.
+ * Notes: This method must be called at the beginning of the main loop, before any rendering methods are used.
+ * Notes: Implementations of this method must call _UpdateLastFrameDuration()
+ * @param UpdateAll Specifies whether the renderer should redraw everything on the next frame.
+ * This feature can be useful if the renderer with Dirty Rectangles works, but sometimes the client may
+ */
+ bool startFrame(bool updateAll = false);
+
+ /**
+ * Ends the rendering of a frame and draws it on the screen.
+ *
+ * This method must be at the end of the main loop. After this call, no further Render method may be called.
+ * This should only be called once for a given previous call to #StartFrame.
+ */
+ bool endFrame();
+
+ // Debug methods
+
+ /**
+ * Draws a line in the frame buffer
+ *
+ * This method must be called between calls to StartFrame() and EndFrame(), and is intended only for debugging
+ * purposes. The line will only appear for a single frame. If the line is to be shown permanently, it must be
+ * called for every frame.
+ * @param Start The starting point of the line
+ * @param End The ending point of the line
+ * @param Color The colour of the line. The default is BS_RGB (255,255,255) (White)
+ */
+ void drawDebugLine(const Vertex &start, const Vertex &end, uint color = BS_RGB(255, 255, 255));
+
+ /**
+ * Creates a thumbnail with the dimensions of 200x125. This will not include the top and bottom of the screen..
+ * the interface boards the the image as a 16th of it's original size.
+ * Notes: This method should only be called after a call to EndFrame(), and before the next call to StartFrame().
+ * The frame buffer must have a resolution of 800x600.
+ * @param Filename The filename for the screenshot
+ */
+ bool saveThumbnailScreenshot(const Common::String &filename);
+
+ /**
+ * Reads the current contents of the frame buffer
+ * Notes: This method is for creating screenshots. It is not very optimised. It should only be called
+ * after a call to EndFrame(), and before the next call to StartFrame().
+ * @param Width Returns the width of the frame buffer
+ * @param Height Returns the height of the frame buffer
+ * @param Data Returns the raw data of the frame buffer as an array of 32-bit colour values.
+ */
+ Graphics::Surface *getScreenshot();
+
+
+ RenderObjectPtr<Panel> getMainPanel();
+
+ /**
+ * Specifies the time (in microseconds) since the last frame has passed
+ */
+ int getLastFrameDurationMicro() const {
+ if (!_timerActive)
+ return 0;
+ return _lastFrameDuration;
+ }
+
+ /**
+ * Specifies the time (in microseconds) the previous frame took
+ */
+ float getLastFrameDuration() const {
+ if (!_timerActive)
+ return 0;
+ return static_cast<float>(_lastFrameDuration) / 1000000.0f;
+ }
+
+ void stopMainTimer() {
+ _timerActive = false;
+ }
+
+ void resumeMainTimer() {
+ _timerActive = true;
+ }
+
+ float getSecondaryFrameDuration() const {
+ return static_cast<float>(_lastFrameDuration) / 1000000.0f;
+ }
+
+ // Accessor methods
+
+ /**
+ * Returns the width of the output buffer in pixels
+ */
+ int getDisplayWidth() const {
+ return _width;
+ }
+
+ /**
+ * Returns the height of the output buffer in pixels
+ */
+ int getDisplayHeight() const {
+ return _height;
+ }
+
+ /**
+ * Returns the bounding box of the output buffer: (0, 0, Width, Height)
+ */
+ Common::Rect &getDisplayRect() {
+ return _screenRect;
+ }
+
+ /**
+ * Returns the bit depth of the output buffer
+ */
+ int getBitDepth() {
+ return _bitDepth;
+ }
+
+ /**
+ * Determines whether the frame buffer change is to be synchronised with Vsync. This is turned on by default.
+ * Notes: In windowed mode, this setting has no effect.
+ * @param Vsync Indicates whether the frame buffer changes are to be synchronised with Vsync.
+ */
+ void setVsync(bool vsync);
+
+ /**
+ * Returns true if V-Sync is on.
+ * Notes: In windowed mode, this setting has no effect.
+ */
+ bool getVsync() const;
+
+ /**
+ * Returns true if the engine is running in Windowed mode.
+ */
+ bool isWindowed() {
+ return _windowed;
+ }
+
+ /**
+ * Fills a rectangular area of the frame buffer with a colour.
+ * Notes: It is possible to create transparent rectangles by passing a colour with an Alpha value of 255.
+ * @param FillRectPtr Pointer to a Common::Rect, which specifies the section of the frame buffer to be filled.
+ * If the rectangle falls partly off-screen, then it is automatically trimmed.
+ * If a NULL value is passed, then the entire image is to be filled.
+ * @param Color The 32-bit colour with which the area is to be filled. The default is BS_RGB(0, 0, 0) (black)
+ * @note FIf the rectangle is not completely inside the screen, it is automatically clipped.
+ */
+ bool fill(const Common::Rect *fillRectPtr = 0, uint color = BS_RGB(0, 0, 0));
+
+ // Debugging Methods
+
+ int getFPSCount() const {
+ return _FPSCounter.getFPS();
+ }
+ int getRepaintedPixels() const {
+ return _repaintedPixels;
+ }
+
+ Graphics::Surface _backSurface;
+ Graphics::Surface *getSurface() { return &_backSurface; }
+
+ Graphics::Surface _frameBuffer;
+ Graphics::Surface *getFrameBuffer() { return &_frameBuffer; }
+
+ Common::MemoryReadStream *_thumbnail;
+ Common::MemoryReadStream *getThumbnail() { return _thumbnail; }
+
+ // Access methods
+
+ /**
+ * Returns the size of a pixel entry in bytes for a particular colour format
+ * @param ColorFormat The desired colour format. The parameter must be of type COLOR_FORMATS
+ * @return Returns the size of a pixel in bytes. If the colour format is unknown, -1 is returned.
+ */
+ static int getPixelSize(GraphicEngine::COLOR_FORMATS colorFormat) {
+ switch (colorFormat) {
+ case GraphicEngine::CF_ARGB32:
+ return 4;
+ default:
+ return -1;
+ }
+ }
+
+ /**
+ * Calculates the length of an image line in bytes, depending on a given colour format.
+ * @param ColorFormat The colour format
+ * @param Width The width of the line in pixels
+ * @return Reflects the length of the line in bytes. If the colour format is
+ * unknown, -1 is returned
+ */
+ static int calcPitch(GraphicEngine::COLOR_FORMATS colorFormat, int width) {
+ switch (colorFormat) {
+ case GraphicEngine::CF_ARGB32:
+ return width * 4;
+
+ default:
+ BS_ASSERT(false);
+ }
+
+ return -1;
+ }
+
+ // Resource-Managing Methods
+ // --------------------------
+ virtual Resource *loadResource(const Common::String &fileName);
+ virtual bool canLoadResource(const Common::String &fileName);
+
+ // Persistence Methods
+ // -------------------
+ virtual bool persist(OutputPersistenceBlock &writer);
+ virtual bool unpersist(InputPersistenceBlock &reader);
+
+ static void ARGBColorToLuaColor(lua_State *L, uint color);
+ static uint luaColorToARGBColor(lua_State *L, int stackIndex);
+
+protected:
+
+ // Display Variables
+ // -----------------
+ int _width;
+ int _height;
+ Common::Rect _screenRect;
+ int _bitDepth;
+ bool _windowed;
+
+ // Debugging Variables
+ // -------------------
+ Framecounter _FPSCounter;
+
+ uint _repaintedPixels;
+
+ /**
+ * Calculates the time since the last frame beginning has passed.
+ */
+ void updateLastFrameDuration();
+
+private:
+ bool registerScriptBindings();
+ void unregisterScriptBindings();
+
+ // LastFrameDuration Variables
+ // ---------------------------
+ uint _lastTimeStamp;
+ uint _lastFrameDuration;
+ bool _timerActive;
+ Common::Array<uint> _frameTimeSamples;
+ uint _frameTimeSampleSlot;
+
+private:
+ byte *_backBuffer;
+
+ RenderObjectPtr<Panel> _mainPanelPtr;
+
+ Common::ScopedPtr<RenderObjectManager> _renderObjectManagerPtr;
+
+ struct DebugLine {
+ DebugLine(const Vertex &start, const Vertex &end, uint color) :
+ _start(start),
+ _end(end),
+ _color(color) {}
+ DebugLine() {}
+
+ Vertex _start;
+ Vertex _end;
+ uint _color;
+ };
+
+ Common::Array<DebugLine> _debugLines;
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/gfx/graphicengine_script.cpp b/engines/sword25/gfx/graphicengine_script.cpp
new file mode 100644
index 0000000000..0814a23871
--- /dev/null
+++ b/engines/sword25/gfx/graphicengine_script.cpp
@@ -0,0 +1,1305 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#include "sword25/kernel/common.h"
+#include "sword25/kernel/kernel.h"
+#include "sword25/script/script.h"
+#include "sword25/script/luabindhelper.h"
+#include "sword25/script/luacallback.h"
+#include "sword25/math/vertex.h"
+
+#include "sword25/gfx/graphicengine.h"
+#include "sword25/gfx/renderobject.h"
+#include "sword25/gfx/bitmap.h"
+#include "sword25/gfx/animation.h"
+#include "sword25/gfx/panel.h"
+#include "sword25/gfx/text.h"
+#include "sword25/gfx/animationtemplate.h"
+#include "sword25/gfx/animationtemplateregistry.h"
+
+namespace Sword25 {
+
+#define BS_LOG_PREFIX "GRAPHICENGINE"
+
+static bool animationDeleteCallback(uint Data);
+static bool animationActionCallback(uint Data);
+static bool animationLoopPointCallback(uint Data);
+
+namespace {
+class ActionCallback : public LuaCallback {
+public:
+ ActionCallback(lua_State *L) : LuaCallback(L) {}
+
+ Common::String Action;
+
+protected:
+ virtual int PreFunctionInvokation(lua_State *L) {
+ lua_pushstring(L, Action.c_str());
+ return 1;
+ }
+};
+
+static LuaCallback *loopPointCallbackPtr = 0; // FIXME: should be turned into GraphicEngine member var
+static ActionCallback *actionCallbackPtr = 0; // FIXME: should be turned into GraphicEngine member var
+}
+
+// Die Strings werden als #defines definiert um Stringkomposition zur Compilezeit zu ermöglichen.
+#define RENDEROBJECT_CLASS_NAME "Gfx.RenderObject"
+#define BITMAP_CLASS_NAME "Gfx.Bitmap"
+#define PANEL_CLASS_NAME "Gfx.Panel"
+#define TEXT_CLASS_NAME "Gfx.Text"
+#define ANIMATION_CLASS_NAME "Gfx.Animation"
+#define ANIMATION_TEMPLATE_CLASS_NAME "Gfx.AnimationTemplate"
+static const char *GFX_LIBRARY_NAME = "Gfx";
+
+// Wie luaL_checkudata, nur ohne dass kein Fehler erzeugt wird.
+static void *my_checkudata(lua_State *L, int ud, const char *tname) {
+ int top = lua_gettop(L);
+
+ void *p = lua_touserdata(L, ud);
+ if (p != NULL) { /* value is a userdata? */
+ if (lua_getmetatable(L, ud)) { /* does it have a metatable? */
+ // lua_getfield(L, LUA_REGISTRYINDEX, tname); /* get correct metatable */
+ LuaBindhelper::getMetatable(L, tname);
+ if (lua_rawequal(L, -1, -2)) { /* does it have the correct mt? */
+ lua_settop(L, top);
+ return p;
+ }
+ }
+ }
+
+ lua_settop(L, top);
+ return NULL;
+}
+
+static void newUintUserData(lua_State *L, uint value) {
+ void *userData = lua_newuserdata(L, sizeof(value));
+ memcpy(userData, &value, sizeof(value));
+}
+
+static AnimationTemplate *checkAnimationTemplate(lua_State *L, int idx = 1) {
+ // Der erste Parameter muss vom Typ userdata sein und die Metatable der Klasse Gfx.AnimationTemplate
+ uint animationTemplateHandle;
+ if ((animationTemplateHandle = *reinterpret_cast<uint *>(my_checkudata(L, idx, ANIMATION_TEMPLATE_CLASS_NAME))) != 0) {
+ AnimationTemplate *animationTemplatePtr = AnimationTemplateRegistry::instance().resolveHandle(animationTemplateHandle);
+ if (!animationTemplatePtr)
+ luaL_error(L, "The animation template with the handle %d does no longer exist.", animationTemplateHandle);
+ return animationTemplatePtr;
+ } else {
+ luaL_argcheck(L, 0, idx, "'" ANIMATION_TEMPLATE_CLASS_NAME "' expected");
+ return 0;
+ }
+}
+
+
+static int newAnimationTemplate(lua_State *L) {
+ uint animationTemplateHandle = AnimationTemplate::create(luaL_checkstring(L, 1));
+ AnimationTemplate *animationTemplatePtr = AnimationTemplateRegistry::instance().resolveHandle(animationTemplateHandle);
+ if (animationTemplatePtr && animationTemplatePtr->isValid()) {
+ newUintUserData(L, animationTemplateHandle);
+ //luaL_getmetatable(L, ANIMATION_TEMPLATE_CLASS_NAME);
+ LuaBindhelper::getMetatable(L, ANIMATION_TEMPLATE_CLASS_NAME);
+ BS_ASSERT(!lua_isnil(L, -1));
+ lua_setmetatable(L, -2);
+ } else {
+ lua_pushnil(L);
+ }
+
+ return 1;
+}
+
+static int at_addFrame(lua_State *L) {
+ AnimationTemplate *pAT = checkAnimationTemplate(L);
+ pAT->addFrame(static_cast<int>(luaL_checknumber(L, 2)));
+ return 0;
+}
+
+static int at_setFrame(lua_State *L) {
+ AnimationTemplate *pAT = checkAnimationTemplate(L);
+ pAT->setFrame(static_cast<int>(luaL_checknumber(L, 2)), static_cast<int>(luaL_checknumber(L, 3)));
+ return 0;
+}
+
+static bool animationTypeStringToNumber(const char *typeString, Animation::ANIMATION_TYPES &result) {
+ if (strcmp(typeString, "jojo") == 0) {
+ result = Animation::AT_JOJO;
+ return true;
+ } else if (strcmp(typeString, "loop") == 0) {
+ result = Animation::AT_LOOP;
+ return true;
+ } else if (strcmp(typeString, "oneshot") == 0) {
+ result = Animation::AT_ONESHOT;
+ return true;
+ } else
+ return false;
+}
+
+static int at_setAnimationType(lua_State *L) {
+ AnimationTemplate *pAT = checkAnimationTemplate(L);
+ Animation::ANIMATION_TYPES animationType;
+ if (animationTypeStringToNumber(luaL_checkstring(L, 2), animationType)) {
+ pAT->setAnimationType(animationType);
+ } else {
+ luaL_argcheck(L, 0, 2, "Invalid animation type");
+ }
+
+ return 0;
+}
+
+static int at_setFPS(lua_State *L) {
+ AnimationTemplate *pAT = checkAnimationTemplate(L);
+ pAT->setFPS(static_cast<int>(luaL_checknumber(L, 2)));
+ return 0;
+}
+
+static int at_finalize(lua_State *L) {
+ AnimationTemplate *pAT = checkAnimationTemplate(L);
+ delete pAT;
+ return 0;
+}
+
+static const luaL_reg ANIMATION_TEMPLATE_METHODS[] = {
+ {"AddFrame", at_addFrame},
+ {"SetFrame", at_setFrame},
+ {"SetAnimationType", at_setAnimationType},
+ {"SetFPS", at_setFPS},
+ {"__gc", at_finalize},
+ {0, 0}
+};
+
+static GraphicEngine *getGE() {
+ Kernel *pKernel = Kernel::getInstance();
+ BS_ASSERT(pKernel);
+ GraphicEngine *pGE = pKernel->getGfx();
+ BS_ASSERT(pGE);
+ return pGE;
+}
+
+static int init(lua_State *L) {
+ GraphicEngine *pGE = getGE();
+
+ switch (lua_gettop(L)) {
+ case 0:
+ lua_pushbooleancpp(L, pGE->init());
+ break;
+ case 1:
+ lua_pushbooleancpp(L, pGE->init(static_cast<int>(luaL_checknumber(L, 1))));
+ break;
+ case 2:
+ lua_pushbooleancpp(L, pGE->init(static_cast<int>(luaL_checknumber(L, 1)), static_cast<int>(luaL_checknumber(L, 2))));
+ break;
+ case 3:
+ lua_pushbooleancpp(L, pGE->init(static_cast<int>(luaL_checknumber(L, 1)), static_cast<int>(luaL_checknumber(L, 2)),
+ static_cast<int>(luaL_checknumber(L, 3))));
+ break;
+ case 4:
+ lua_pushbooleancpp(L, pGE->init(static_cast<int>(luaL_checknumber(L, 1)), static_cast<int>(luaL_checknumber(L, 2)),
+ static_cast<int>(luaL_checknumber(L, 3)), static_cast<int>(luaL_checknumber(L, 4))));
+ break;
+ default:
+ lua_pushbooleancpp(L, pGE->init(static_cast<int>(luaL_checknumber(L, 1)), static_cast<int>(luaL_checknumber(L, 2)),
+ static_cast<int>(luaL_checknumber(L, 3)), static_cast<int>(luaL_checknumber(L, 4)),
+ lua_tobooleancpp(L, 5)));
+ }
+
+
+#ifdef DEBUG
+ int __startStackDepth = lua_gettop(L);
+#endif
+
+ // Main-Panel zum Gfx-Modul hinzufügen
+ RenderObjectPtr<Panel> mainPanelPtr(getGE()->getMainPanel());
+ BS_ASSERT(mainPanelPtr.isValid());
+
+ lua_pushstring(L, GFX_LIBRARY_NAME);
+ lua_gettable(L, LUA_GLOBALSINDEX);
+ BS_ASSERT(!lua_isnil(L, -1));
+
+ newUintUserData(L, mainPanelPtr->getHandle());
+ BS_ASSERT(!lua_isnil(L, -1));
+ // luaL_getmetatable(L, PANEL_CLASS_NAME);
+ LuaBindhelper::getMetatable(L, PANEL_CLASS_NAME);
+ BS_ASSERT(!lua_isnil(L, -1));
+ lua_setmetatable(L, -2);
+
+ lua_pushstring(L, "MainPanel");
+ lua_insert(L, -2);
+ lua_settable(L, -3);
+
+ lua_pop(L, 1);
+
+#ifdef DEBUG
+ BS_ASSERT(__startStackDepth == lua_gettop(L));
+#endif
+
+ return 1;
+}
+
+static int startFrame(lua_State *L) {
+ GraphicEngine *pGE = getGE();
+
+ if (lua_gettop(L) == 0)
+ lua_pushbooleancpp(L, pGE->startFrame());
+ else
+ lua_pushbooleancpp(L, pGE->startFrame(lua_tobooleancpp(L, 1)));
+
+ return 1;
+}
+
+static int endFrame(lua_State *L) {
+ GraphicEngine *pGE = getGE();
+
+ lua_pushbooleancpp(L, pGE->endFrame());
+
+ return 1;
+}
+
+static int drawDebugLine(lua_State *L) {
+ GraphicEngine *pGE = getGE();
+
+ Vertex start;
+ Vertex end;
+ Vertex::luaVertexToVertex(L, 1, start);
+ Vertex::luaVertexToVertex(L, 2, end);
+ pGE->drawDebugLine(start, end, GraphicEngine::luaColorToARGBColor(L, 3));
+
+ return 0;
+}
+
+static int getDisplayWidth(lua_State *L) {
+ GraphicEngine *pGE = getGE();
+
+ lua_pushnumber(L, pGE->getDisplayWidth());
+
+ return 1;
+}
+
+static int getDisplayHeight(lua_State *L) {
+ GraphicEngine *pGE = getGE();
+
+ lua_pushnumber(L, pGE->getDisplayHeight());
+
+ return 1;
+}
+
+static int getBitDepth(lua_State *L) {
+ GraphicEngine *pGE = getGE();
+
+ lua_pushnumber(L, pGE->getBitDepth());
+
+ return 1;
+}
+
+static int setVsync(lua_State *L) {
+ GraphicEngine *pGE = getGE();
+
+ pGE->setVsync(lua_tobooleancpp(L, 1));
+
+ return 0;
+}
+
+static int isVsync(lua_State *L) {
+ GraphicEngine *pGE = getGE();
+
+ lua_pushbooleancpp(L, pGE->getVsync());
+
+ return 1;
+}
+
+static int isWindowed(lua_State *L) {
+ GraphicEngine *pGE = getGE();
+
+ lua_pushbooleancpp(L, pGE->isWindowed());
+
+ return 1;
+}
+
+static int getFPSCount(lua_State *L) {
+ GraphicEngine *pGE = getGE();
+
+ lua_pushnumber(L, pGE->getFPSCount());
+
+ return 1;
+}
+
+static int getLastFrameDuration(lua_State *L) {
+ GraphicEngine *pGE = getGE();
+
+ lua_pushnumber(L, pGE->getLastFrameDuration());
+
+ return 1;
+}
+
+static int stopMainTimer(lua_State *L) {
+ GraphicEngine *pGE = getGE();
+ pGE->stopMainTimer();
+ return 0;
+}
+
+static int resumeMainTimer(lua_State *L) {
+ GraphicEngine *pGE = getGE();
+ pGE->resumeMainTimer();
+ return 0;
+}
+
+static int getSecondaryFrameDuration(lua_State *L) {
+ GraphicEngine *pGE = getGE();
+
+ lua_pushnumber(L, pGE->getSecondaryFrameDuration());
+
+ return 1;
+}
+
+static int saveScreenshot(lua_State *L) {
+ // This is used by system/debug.lua only. We do not implement this; support
+ // for taking screenshots is a backend feature.
+ lua_pushbooleancpp(L, false);
+
+ return 1;
+}
+
+static int saveThumbnailScreenshot(lua_State *L) {
+ GraphicEngine *pGE = getGE();
+ lua_pushbooleancpp(L, pGE->saveThumbnailScreenshot(luaL_checkstring(L, 1)));
+ return 1;
+}
+
+static int getRepaintedPixels(lua_State *L) {
+ GraphicEngine *pGE = getGE();
+ lua_pushnumber(L, static_cast<lua_Number>(pGE->getRepaintedPixels()));
+ return 1;
+}
+
+static const luaL_reg GFX_FUNCTIONS[] = {
+ {"Init", init},
+ {"StartFrame", startFrame},
+ {"EndFrame", endFrame},
+ {"DrawDebugLine", drawDebugLine},
+ {"SetVsync", setVsync},
+ {"GetDisplayWidth", getDisplayWidth},
+ {"GetDisplayHeight", getDisplayHeight},
+ {"GetBitDepth", getBitDepth},
+ {"IsVsync", isVsync},
+ {"IsWindowed", isWindowed},
+ {"GetFPSCount", getFPSCount},
+ {"GetLastFrameDuration", getLastFrameDuration},
+ {"StopMainTimer", stopMainTimer},
+ {"ResumeMainTimer", resumeMainTimer},
+ {"GetSecondaryFrameDuration", getSecondaryFrameDuration},
+ {"SaveScreenshot", saveScreenshot},
+ {"NewAnimationTemplate", newAnimationTemplate},
+ {"GetRepaintedPixels", getRepaintedPixels},
+ {"SaveThumbnailScreenshot", saveThumbnailScreenshot},
+ {0, 0}
+};
+
+static RenderObjectPtr<RenderObject> checkRenderObject(lua_State *L, bool errorIfRemoved = true) {
+ // Der erste Parameter muss vom Typ userdata sein und die Metatable einer Klasse haben, die von Gfx.RenderObject "erbt".
+ uint *userDataPtr;
+ if ((userDataPtr = (uint *) my_checkudata(L, 1, BITMAP_CLASS_NAME)) != 0 ||
+ (userDataPtr = (uint *) my_checkudata(L, 1, ANIMATION_CLASS_NAME)) != 0 ||
+ (userDataPtr = (uint *) my_checkudata(L, 1, PANEL_CLASS_NAME)) != 0 ||
+ (userDataPtr = (uint *) my_checkudata(L, 1, TEXT_CLASS_NAME)) != 0) {
+ RenderObjectPtr<RenderObject> roPtr(*userDataPtr);
+ if (roPtr.isValid())
+ return roPtr;
+ else {
+ if (errorIfRemoved)
+ luaL_error(L, "The renderobject with the handle %d does no longer exist.", *userDataPtr);
+ }
+ } else {
+ luaL_argcheck(L, 0, 1, "'" RENDEROBJECT_CLASS_NAME "' expected");
+ }
+
+ return RenderObjectPtr<RenderObject>();
+}
+
+static int ro_setPos(lua_State *L) {
+ RenderObjectPtr<RenderObject> roPtr = checkRenderObject(L);
+ BS_ASSERT(roPtr.isValid());
+ Vertex pos;
+ Vertex::luaVertexToVertex(L, 2, pos);
+ roPtr->setPos(pos.x, pos.y);
+ return 0;
+}
+
+static int ro_setX(lua_State *L) {
+ RenderObjectPtr<RenderObject> roPtr = checkRenderObject(L);
+ BS_ASSERT(roPtr.isValid());
+ roPtr->setX(static_cast<int>(luaL_checknumber(L, 2)));
+ return 0;
+}
+
+static int ro_setY(lua_State *L) {
+ RenderObjectPtr<RenderObject> roPtr = checkRenderObject(L);
+ BS_ASSERT(roPtr.isValid());
+ roPtr->setY(static_cast<int>(luaL_checknumber(L, 2)));
+ return 0;
+}
+
+static int ro_setZ(lua_State *L) {
+ RenderObjectPtr<RenderObject> roPtr = checkRenderObject(L);
+ BS_ASSERT(roPtr.isValid());
+ roPtr->setZ(static_cast<int>(luaL_checknumber(L, 2)));
+ return 0;
+}
+
+static int ro_setVisible(lua_State *L) {
+ RenderObjectPtr<RenderObject> roPtr = checkRenderObject(L);
+ BS_ASSERT(roPtr.isValid());
+ roPtr->setVisible(lua_tobooleancpp(L, 2));
+ return 0;
+}
+
+static int ro_getX(lua_State *L) {
+ RenderObjectPtr<RenderObject> roPtr = checkRenderObject(L);
+ BS_ASSERT(roPtr.isValid());
+ lua_pushnumber(L, roPtr->getX());
+
+ return 1;
+}
+
+static int ro_getY(lua_State *L) {
+ RenderObjectPtr<RenderObject> roPtr = checkRenderObject(L);
+ BS_ASSERT(roPtr.isValid());
+ lua_pushnumber(L, roPtr->getY());
+
+ return 1;
+}
+
+static int ro_getZ(lua_State *L) {
+ RenderObjectPtr<RenderObject> roPtr = checkRenderObject(L);
+ BS_ASSERT(roPtr.isValid());
+ lua_pushnumber(L, roPtr->getZ());
+
+ return 1;
+}
+
+static int ro_getAbsoluteX(lua_State *L) {
+ RenderObjectPtr<RenderObject> roPtr = checkRenderObject(L);
+ BS_ASSERT(roPtr.isValid());
+ lua_pushnumber(L, roPtr->getAbsoluteX());
+
+ return 1;
+}
+
+static int ro_getAbsoluteY(lua_State *L) {
+ RenderObjectPtr<RenderObject> roPtr = checkRenderObject(L);
+ BS_ASSERT(roPtr.isValid());
+ lua_pushnumber(L, roPtr->getAbsoluteY());
+
+ return 1;
+}
+
+static int ro_getWidth(lua_State *L) {
+ RenderObjectPtr<RenderObject> roPtr = checkRenderObject(L);
+ BS_ASSERT(roPtr.isValid());
+ lua_pushnumber(L, roPtr->getWidth());
+
+ return 1;
+}
+
+static int ro_getHeight(lua_State *L) {
+ RenderObjectPtr<RenderObject> roPtr = checkRenderObject(L);
+ BS_ASSERT(roPtr.isValid());
+ lua_pushnumber(L, roPtr->getHeight());
+
+ return 1;
+}
+
+static int ro_isVisible(lua_State *L) {
+ RenderObjectPtr<RenderObject> roPtr = checkRenderObject(L);
+ BS_ASSERT(roPtr.isValid());
+ lua_pushbooleancpp(L, roPtr->isVisible());
+
+ return 1;
+}
+
+static int ro_addPanel(lua_State *L) {
+ RenderObjectPtr<RenderObject> roPtr = checkRenderObject(L);
+ BS_ASSERT(roPtr.isValid());
+ RenderObjectPtr<Panel> panelPtr = roPtr->addPanel(static_cast<int>(luaL_checknumber(L, 2)),
+ static_cast<int>(luaL_checknumber(L, 3)),
+ GraphicEngine::luaColorToARGBColor(L, 4));
+ if (panelPtr.isValid()) {
+ newUintUserData(L, panelPtr->getHandle());
+ // luaL_getmetatable(L, PANEL_CLASS_NAME);
+ LuaBindhelper::getMetatable(L, PANEL_CLASS_NAME);
+ BS_ASSERT(!lua_isnil(L, -1));
+ lua_setmetatable(L, -2);
+ } else
+ lua_pushnil(L);
+
+ return 1;
+}
+
+static int ro_addBitmap(lua_State *L) {
+ RenderObjectPtr<RenderObject> roPtr = checkRenderObject(L);
+ BS_ASSERT(roPtr.isValid());
+ RenderObjectPtr<Bitmap> bitmaPtr = roPtr->addBitmap(luaL_checkstring(L, 2));
+ if (bitmaPtr.isValid()) {
+ newUintUserData(L, bitmaPtr->getHandle());
+ // luaL_getmetatable(L, BITMAP_CLASS_NAME);
+ LuaBindhelper::getMetatable(L, BITMAP_CLASS_NAME);
+ BS_ASSERT(!lua_isnil(L, -1));
+ lua_setmetatable(L, -2);
+ } else
+ lua_pushnil(L);
+
+ return 1;
+}
+
+static int ro_addText(lua_State *L) {
+ RenderObjectPtr<RenderObject> roPtr = checkRenderObject(L);
+ BS_ASSERT(roPtr.isValid());
+
+ RenderObjectPtr<Text> textPtr;
+ if (lua_gettop(L) >= 3)
+ textPtr = roPtr->addText(luaL_checkstring(L, 2), luaL_checkstring(L, 3));
+ else
+ textPtr = roPtr->addText(luaL_checkstring(L, 2));
+
+ if (textPtr.isValid()) {
+ newUintUserData(L, textPtr->getHandle());
+ // luaL_getmetatable(L, TEXT_CLASS_NAME);
+ LuaBindhelper::getMetatable(L, TEXT_CLASS_NAME);
+ BS_ASSERT(!lua_isnil(L, -1));
+ lua_setmetatable(L, -2);
+ } else
+ lua_pushnil(L);
+
+ return 1;
+}
+
+static int ro_addAnimation(lua_State *L) {
+ RenderObjectPtr<RenderObject> roPtr = checkRenderObject(L);
+ BS_ASSERT(roPtr.isValid());
+
+ RenderObjectPtr<Animation> animationPtr;
+ if (lua_type(L, 2) == LUA_TUSERDATA)
+ animationPtr = roPtr->addAnimation(*checkAnimationTemplate(L, 2));
+ else
+ animationPtr = roPtr->addAnimation(luaL_checkstring(L, 2));
+
+ if (animationPtr.isValid()) {
+ newUintUserData(L, animationPtr->getHandle());
+ // luaL_getmetatable(L, ANIMATION_CLASS_NAME);
+ LuaBindhelper::getMetatable(L, ANIMATION_CLASS_NAME);
+ BS_ASSERT(!lua_isnil(L, -1));
+ lua_setmetatable(L, -2);
+
+ // Alle Animationscallbacks registrieren.
+ animationPtr->setCallbacks();
+ } else
+ lua_pushnil(L);
+
+ return 1;
+}
+
+void Animation::setCallbacks() {
+ _actionCallback = animationActionCallback;
+ _loopPointCallback = animationLoopPointCallback;
+ _deleteCallback = animationDeleteCallback;
+}
+
+static const luaL_reg RENDEROBJECT_METHODS[] = {
+ {"AddAnimation", ro_addAnimation},
+ {"AddText", ro_addText},
+ {"AddBitmap", ro_addBitmap},
+ {"AddPanel", ro_addPanel},
+ {"SetPos", ro_setPos},
+ {"SetX", ro_setX},
+ {"SetY", ro_setY},
+ {"SetZ", ro_setZ},
+ {"SetVisible", ro_setVisible},
+ {"GetX", ro_getX},
+ {"GetY", ro_getY},
+ {"GetZ", ro_getZ},
+ {"GetAbsoluteX", ro_getAbsoluteX},
+ {"GetAbsoluteY", ro_getAbsoluteY},
+ {"GetWidth", ro_getWidth},
+ {"GetHeight", ro_getHeight},
+ {"IsVisible", ro_isVisible},
+ {0, 0}
+};
+
+static RenderObjectPtr<Panel> checkPanel(lua_State *L) {
+ // Der erste Parameter muss vom Typ userdata sein und die Metatable der Klasse Gfx.Panel
+ uint *userDataPtr;
+ if ((userDataPtr = (uint *)my_checkudata(L, 1, PANEL_CLASS_NAME)) != 0) {
+ RenderObjectPtr<RenderObject> roPtr(*userDataPtr);
+ if (roPtr.isValid()) {
+ return roPtr->toPanel();
+ } else
+ luaL_error(L, "The panel with the handle %d does no longer exist.", *userDataPtr);
+ } else {
+ luaL_argcheck(L, 0, 1, "'" PANEL_CLASS_NAME "' expected");
+ }
+
+ return RenderObjectPtr<Panel>();
+}
+
+static int p_getColor(lua_State *L) {
+ RenderObjectPtr<Panel> PanelPtr = checkPanel(L);
+ BS_ASSERT(PanelPtr.isValid());
+ GraphicEngine::ARGBColorToLuaColor(L, PanelPtr->getColor());
+
+ return 1;
+}
+
+static int p_setColor(lua_State *L) {
+ RenderObjectPtr<Panel> PanelPtr = checkPanel(L);
+ BS_ASSERT(PanelPtr.isValid());
+ PanelPtr->setColor(GraphicEngine::luaColorToARGBColor(L, 2));
+ return 0;
+}
+
+static int p_remove(lua_State *L) {
+ RenderObjectPtr<RenderObject> roPtr = checkRenderObject(L);
+ BS_ASSERT(roPtr.isValid());
+ roPtr.erase();
+ return 0;
+}
+
+static const luaL_reg PANEL_METHODS[] = {
+ {"GetColor", p_getColor},
+ {"SetColor", p_setColor},
+ {"Remove", p_remove},
+ {0, 0}
+};
+
+static RenderObjectPtr<Bitmap> checkBitmap(lua_State *L) {
+ // Der erste Parameter muss vom Typ userdata sein und die Metatable der Klasse Gfx.Bitmap
+ uint *userDataPtr;
+ if ((userDataPtr = (uint *)my_checkudata(L, 1, BITMAP_CLASS_NAME)) != 0) {
+ RenderObjectPtr<RenderObject> roPtr(*userDataPtr);
+ if (roPtr.isValid()) {
+ return roPtr->toBitmap();
+ } else
+ luaL_error(L, "The bitmap with the handle %d does no longer exist.", *userDataPtr);
+ } else {
+ luaL_argcheck(L, 0, 1, "'" BITMAP_CLASS_NAME "' expected");
+ }
+
+ return RenderObjectPtr<Bitmap>();
+}
+
+static int b_setAlpha(lua_State *L) {
+ RenderObjectPtr<Bitmap> bitmapPtr = checkBitmap(L);
+ BS_ASSERT(bitmapPtr.isValid());
+ bitmapPtr->setAlpha(static_cast<uint>(luaL_checknumber(L, 2)));
+ return 0;
+}
+
+static int b_setTintColor(lua_State *L) {
+ RenderObjectPtr<Bitmap> bitmapPtr = checkBitmap(L);
+ BS_ASSERT(bitmapPtr.isValid());
+ bitmapPtr->setModulationColor(GraphicEngine::luaColorToARGBColor(L, 2));
+ return 0;
+}
+
+static int b_setScaleFactor(lua_State *L) {
+ RenderObjectPtr<Bitmap> bitmapPtr = checkBitmap(L);
+ BS_ASSERT(bitmapPtr.isValid());
+ bitmapPtr->setScaleFactor(static_cast<float>(luaL_checknumber(L, 2)));
+ return 0;
+}
+
+static int b_setScaleFactorX(lua_State *L) {
+ RenderObjectPtr<Bitmap> bitmapPtr = checkBitmap(L);
+ BS_ASSERT(bitmapPtr.isValid());
+ bitmapPtr->setScaleFactorX(static_cast<float>(luaL_checknumber(L, 2)));
+ return 0;
+}
+
+static int b_setScaleFactorY(lua_State *L) {
+ RenderObjectPtr<Bitmap> bitmapPtr = checkBitmap(L);
+ BS_ASSERT(bitmapPtr.isValid());
+ bitmapPtr->setScaleFactorY(static_cast<float>(luaL_checknumber(L, 2)));
+ return 0;
+}
+
+static int b_setFlipH(lua_State *L) {
+ RenderObjectPtr<Bitmap> bitmapPtr = checkBitmap(L);
+ BS_ASSERT(bitmapPtr.isValid());
+ bitmapPtr->setFlipH(lua_tobooleancpp(L, 2));
+ return 0;
+}
+
+static int b_setFlipV(lua_State *L) {
+ RenderObjectPtr<Bitmap> bitmapPtr = checkBitmap(L);
+ BS_ASSERT(bitmapPtr.isValid());
+ bitmapPtr->setFlipV(lua_tobooleancpp(L, 2));
+ return 0;
+}
+
+static int b_getAlpha(lua_State *L) {
+ RenderObjectPtr<Bitmap> bitmapPtr = checkBitmap(L);
+ BS_ASSERT(bitmapPtr.isValid());
+ lua_pushnumber(L, bitmapPtr->getAlpha());
+ return 1;
+}
+
+static int b_getTintColor(lua_State *L) {
+ RenderObjectPtr<Bitmap> bitmapPtr = checkBitmap(L);
+ BS_ASSERT(bitmapPtr.isValid());
+ GraphicEngine::ARGBColorToLuaColor(L, bitmapPtr->getModulationColor());
+ return 1;
+}
+
+static int b_getScaleFactorX(lua_State *L) {
+ RenderObjectPtr<Bitmap> bitmapPtr = checkBitmap(L);
+ BS_ASSERT(bitmapPtr.isValid());
+ lua_pushnumber(L, bitmapPtr->getScaleFactorX());
+ return 1;
+}
+
+static int b_getScaleFactorY(lua_State *L) {
+ RenderObjectPtr<Bitmap> bitmapPtr = checkBitmap(L);
+ BS_ASSERT(bitmapPtr.isValid());
+ lua_pushnumber(L, bitmapPtr->getScaleFactorY());
+ return 1;
+}
+
+static int b_isFlipH(lua_State *L) {
+ RenderObjectPtr<Bitmap> bitmapPtr = checkBitmap(L);
+ BS_ASSERT(bitmapPtr.isValid());
+ lua_pushbooleancpp(L, bitmapPtr->isFlipH());
+ return 1;
+}
+
+static int b_isFlipV(lua_State *L) {
+ RenderObjectPtr<Bitmap> bitmapPtr = checkBitmap(L);
+ BS_ASSERT(bitmapPtr.isValid());
+ lua_pushbooleancpp(L, bitmapPtr->isFlipV());
+ return 1;
+}
+
+static int b_getPixel(lua_State *L) {
+ RenderObjectPtr<Bitmap> bitmapPtr = checkBitmap(L);
+ BS_ASSERT(bitmapPtr.isValid());
+ Vertex Pos;
+ Vertex::luaVertexToVertex(L, 2, Pos);
+ GraphicEngine::ARGBColorToLuaColor(L, bitmapPtr->getPixel(Pos.x, Pos.y));
+ return 1;
+}
+
+static int b_isScalingAllowed(lua_State *L) {
+ RenderObjectPtr<Bitmap> bitmapPtr = checkBitmap(L);
+ BS_ASSERT(bitmapPtr.isValid());
+ lua_pushbooleancpp(L, bitmapPtr->isScalingAllowed());
+ return 1;
+}
+
+static int b_isAlphaAllowed(lua_State *L) {
+ RenderObjectPtr<Bitmap> bitmapPtr = checkBitmap(L);
+ BS_ASSERT(bitmapPtr.isValid());
+ lua_pushbooleancpp(L, bitmapPtr->isAlphaAllowed());
+ return 1;
+}
+
+static int b_isTintingAllowed(lua_State *L) {
+ RenderObjectPtr<Bitmap> bitmapPtr = checkBitmap(L);
+ BS_ASSERT(bitmapPtr.isValid());
+ lua_pushbooleancpp(L, bitmapPtr->isColorModulationAllowed());
+ return 1;
+}
+
+static int b_remove(lua_State *L) {
+ RenderObjectPtr<RenderObject> roPtr = checkRenderObject(L);
+ BS_ASSERT(roPtr.isValid());
+ roPtr.erase();
+ return 0;
+}
+
+static const luaL_reg BITMAP_METHODS[] = {
+ {"SetAlpha", b_setAlpha},
+ {"SetTintColor", b_setTintColor},
+ {"SetScaleFactor", b_setScaleFactor},
+ {"SetScaleFactorX", b_setScaleFactorX},
+ {"SetScaleFactorY", b_setScaleFactorY},
+ {"SetFlipH", b_setFlipH},
+ {"SetFlipV", b_setFlipV},
+ {"GetAlpha", b_getAlpha},
+ {"GetTintColor", b_getTintColor},
+ {"GetScaleFactorX", b_getScaleFactorX},
+ {"GetScaleFactorY", b_getScaleFactorY},
+ {"IsFlipH", b_isFlipH},
+ {"IsFlipV", b_isFlipV},
+ {"GetPixel", b_getPixel},
+ {"IsScalingAllowed", b_isScalingAllowed},
+ {"IsAlphaAllowed", b_isAlphaAllowed},
+ {"IsTintingAllowed", b_isTintingAllowed},
+ {"Remove", b_remove},
+ {0, 0}
+};
+
+static RenderObjectPtr<Animation> checkAnimation(lua_State *L) {
+ // Der erste Parameter muss vom Typ userdata sein und die Metatable der Klasse Gfx.Animation
+ uint *userDataPtr;
+ if ((userDataPtr = (uint *)my_checkudata(L, 1, ANIMATION_CLASS_NAME)) != 0) {
+ RenderObjectPtr<RenderObject> roPtr(*userDataPtr);
+ if (roPtr.isValid())
+ return roPtr->toAnimation();
+ else {
+ luaL_error(L, "The animation with the handle %d does no longer exist.", *userDataPtr);
+ }
+ } else {
+ luaL_argcheck(L, 0, 1, "'" ANIMATION_CLASS_NAME "' expected");
+ }
+
+ return RenderObjectPtr<Animation>();
+}
+
+static int a_play(lua_State *L) {
+ RenderObjectPtr<Animation> animationPtr = checkAnimation(L);
+ BS_ASSERT(animationPtr.isValid());
+ animationPtr->play();
+ return 0;
+}
+
+static int a_pause(lua_State *L) {
+ RenderObjectPtr<Animation> animationPtr = checkAnimation(L);
+ BS_ASSERT(animationPtr.isValid());
+ animationPtr->pause();
+ return 0;
+}
+
+static int a_stop(lua_State *L) {
+ RenderObjectPtr<Animation> animationPtr = checkAnimation(L);
+ BS_ASSERT(animationPtr.isValid());
+ animationPtr->stop();
+ return 0;
+}
+
+static int a_setFrame(lua_State *L) {
+ RenderObjectPtr<Animation> animationPtr = checkAnimation(L);
+ BS_ASSERT(animationPtr.isValid());
+ animationPtr->setFrame(static_cast<uint>(luaL_checknumber(L, 2)));
+ return 0;
+}
+
+static int a_setAlpha(lua_State *L) {
+ RenderObjectPtr<Animation> animationPtr = checkAnimation(L);
+ BS_ASSERT(animationPtr.isValid());
+ animationPtr->setAlpha(static_cast<int>(luaL_checknumber(L, 2)));
+ return 0;
+}
+
+static int a_setTintColor(lua_State *L) {
+ RenderObjectPtr<Animation> animationPtr = checkAnimation(L);
+ BS_ASSERT(animationPtr.isValid());
+ animationPtr->setModulationColor(GraphicEngine::luaColorToARGBColor(L, 2));
+ return 0;
+}
+
+static int a_setScaleFactor(lua_State *L) {
+ RenderObjectPtr<Animation> animationPtr = checkAnimation(L);
+ BS_ASSERT(animationPtr.isValid());
+ animationPtr->setScaleFactor(static_cast<float>(luaL_checknumber(L, 2)));
+ return 0;
+}
+
+static int a_setScaleFactorX(lua_State *L) {
+ RenderObjectPtr<Animation> animationPtr = checkAnimation(L);
+ BS_ASSERT(animationPtr.isValid());
+ animationPtr->setScaleFactorX(static_cast<float>(luaL_checknumber(L, 2)));
+ return 0;
+}
+
+static int a_setScaleFactorY(lua_State *L) {
+ RenderObjectPtr<Animation> animationPtr = checkAnimation(L);
+ BS_ASSERT(animationPtr.isValid());
+ animationPtr->setScaleFactorY(static_cast<float>(luaL_checknumber(L, 2)));
+ return 0;
+}
+
+static int a_getScaleFactorX(lua_State *L) {
+ RenderObjectPtr<Animation> animationPtr = checkAnimation(L);
+ BS_ASSERT(animationPtr.isValid());
+ lua_pushnumber(L, animationPtr->getScaleFactorX());
+ return 1;
+}
+
+static int a_getScaleFactorY(lua_State *L) {
+ RenderObjectPtr<Animation> animationPtr = checkAnimation(L);
+ BS_ASSERT(animationPtr.isValid());
+ lua_pushnumber(L, animationPtr->getScaleFactorY());
+ return 1;
+}
+
+static int a_getAnimationType(lua_State *L) {
+ RenderObjectPtr<Animation> animationPtr = checkAnimation(L);
+ BS_ASSERT(animationPtr.isValid());
+ switch (animationPtr->getAnimationType()) {
+ case Animation::AT_JOJO:
+ lua_pushstring(L, "jojo");
+ break;
+ case Animation::AT_LOOP:
+ lua_pushstring(L, "loop");
+ break;
+ case Animation::AT_ONESHOT:
+ lua_pushstring(L, "oneshot");
+ break;
+ default:
+ BS_ASSERT(false);
+ }
+ return 1;
+}
+
+static int a_getFPS(lua_State *L) {
+ RenderObjectPtr<Animation> animationPtr = checkAnimation(L);
+ BS_ASSERT(animationPtr.isValid());
+ lua_pushnumber(L, animationPtr->getFPS());
+ return 1;
+}
+
+static int a_getFrameCount(lua_State *L) {
+ RenderObjectPtr<Animation> animationPtr = checkAnimation(L);
+ BS_ASSERT(animationPtr.isValid());
+ lua_pushnumber(L, animationPtr->getFrameCount());
+ return 1;
+}
+
+static int a_isScalingAllowed(lua_State *L) {
+ RenderObjectPtr<Animation> animationPtr = checkAnimation(L);
+ BS_ASSERT(animationPtr.isValid());
+ lua_pushbooleancpp(L, animationPtr->isScalingAllowed());
+ return 1;
+}
+
+static int a_isAlphaAllowed(lua_State *L) {
+ RenderObjectPtr<Animation> animationPtr = checkAnimation(L);
+ BS_ASSERT(animationPtr.isValid());
+ lua_pushbooleancpp(L, animationPtr->isAlphaAllowed());
+ return 1;
+}
+
+static int a_isTintingAllowed(lua_State *L) {
+ RenderObjectPtr<Animation> animationPtr = checkAnimation(L);
+ BS_ASSERT(animationPtr.isValid());
+ lua_pushbooleancpp(L, animationPtr->isColorModulationAllowed());
+ return 1;
+}
+
+static int a_getCurrentFrame(lua_State *L) {
+ RenderObjectPtr<Animation> animationPtr = checkAnimation(L);
+ BS_ASSERT(animationPtr.isValid());
+ lua_pushnumber(L, animationPtr->getCurrentFrame());
+ return 1;
+}
+
+static int a_getCurrentAction(lua_State *L) {
+ RenderObjectPtr<Animation> animationPtr = checkAnimation(L);
+ BS_ASSERT(animationPtr.isValid());
+ lua_pushstring(L, animationPtr->getCurrentAction().c_str());
+ return 1;
+}
+
+static int a_isPlaying(lua_State *L) {
+ RenderObjectPtr<Animation> animationPtr = checkAnimation(L);
+ BS_ASSERT(animationPtr.isValid());
+ lua_pushbooleancpp(L, animationPtr->isRunning());
+ return 1;
+}
+
+static bool animationLoopPointCallback(uint handle) {
+ lua_State *L = static_cast<lua_State *>(Kernel::getInstance()->getScript()->getScriptObject());
+ loopPointCallbackPtr->invokeCallbackFunctions(L, handle);
+
+ return true;
+}
+
+static int a_registerLoopPointCallback(lua_State *L) {
+ RenderObjectPtr<Animation> animationPtr = checkAnimation(L);
+ BS_ASSERT(animationPtr.isValid());
+ luaL_checktype(L, 2, LUA_TFUNCTION);
+
+ lua_pushvalue(L, 2);
+ loopPointCallbackPtr->registerCallbackFunction(L, animationPtr->getHandle());
+
+ return 0;
+}
+
+static int a_unregisterLoopPointCallback(lua_State *L) {
+ RenderObjectPtr<Animation> animationPtr = checkAnimation(L);
+ BS_ASSERT(animationPtr.isValid());
+ luaL_checktype(L, 2, LUA_TFUNCTION);
+
+ lua_pushvalue(L, 2);
+ loopPointCallbackPtr->unregisterCallbackFunction(L, animationPtr->getHandle());
+
+ return 0;
+}
+
+static bool animationActionCallback(uint Handle) {
+ RenderObjectPtr<Animation> animationPtr(Handle);
+ if (animationPtr.isValid()) {
+ actionCallbackPtr->Action = animationPtr->getCurrentAction();
+ lua_State *L = static_cast<lua_State *>(Kernel::getInstance()->getScript()->getScriptObject());
+ actionCallbackPtr->invokeCallbackFunctions(L, animationPtr->getHandle());
+ }
+
+ return true;
+}
+
+static int a_registerActionCallback(lua_State *L) {
+ RenderObjectPtr<Animation> animationPtr = checkAnimation(L);
+ BS_ASSERT(animationPtr.isValid());
+ luaL_checktype(L, 2, LUA_TFUNCTION);
+
+ lua_pushvalue(L, 2);
+ actionCallbackPtr->registerCallbackFunction(L, animationPtr->getHandle());
+
+ return 0;
+}
+
+static int a_unregisterActionCallback(lua_State *L) {
+ RenderObjectPtr<Animation> animationPtr = checkAnimation(L);
+ BS_ASSERT(animationPtr.isValid());
+ luaL_checktype(L, 2, LUA_TFUNCTION);
+
+ lua_pushvalue(L, 2);
+ actionCallbackPtr->unregisterCallbackFunction(L, animationPtr->getHandle());
+
+ return 0;
+}
+
+static bool animationDeleteCallback(uint Handle) {
+ lua_State *L = static_cast<lua_State *>(Kernel::getInstance()->getScript()->getScriptObject());
+ loopPointCallbackPtr->removeAllObjectCallbacks(L, Handle);
+
+ return true;
+}
+
+static int a_remove(lua_State *L) {
+ RenderObjectPtr<Animation> animationPtr = checkAnimation(L);
+ BS_ASSERT(animationPtr.isValid());
+ animationPtr.erase();
+ return 0;
+}
+
+static const luaL_reg ANIMATION_METHODS[] = {
+ {"Play", a_play},
+ {"Pause", a_pause},
+ {"Stop", a_stop},
+ {"SetFrame", a_setFrame},
+ {"SetAlpha", a_setAlpha},
+ {"SetTintColor", a_setTintColor},
+ {"SetScaleFactor", a_setScaleFactor},
+ {"SetScaleFactorX", a_setScaleFactorX},
+ {"SetScaleFactorY", a_setScaleFactorY},
+ {"GetScaleFactorX", a_getScaleFactorX},
+ {"GetScaleFactorY", a_getScaleFactorY},
+ {"GetAnimationType", a_getAnimationType},
+ {"GetFPS", a_getFPS},
+ {"GetFrameCount", a_getFrameCount},
+ {"IsScalingAllowed", a_isScalingAllowed},
+ {"IsAlphaAllowed", a_isAlphaAllowed},
+ {"IsTintingAllowed", a_isTintingAllowed},
+ {"GetCurrentFrame", a_getCurrentFrame},
+ {"GetCurrentAction", a_getCurrentAction},
+ {"IsPlaying", a_isPlaying},
+ {"RegisterLoopPointCallback", a_registerLoopPointCallback},
+ {"UnregisterLoopPointCallback", a_unregisterLoopPointCallback},
+ {"RegisterActionCallback", a_registerActionCallback},
+ {"UnregisterActionCallback", a_unregisterActionCallback},
+ {"Remove", a_remove},
+ {0, 0}
+};
+
+static RenderObjectPtr<Text> checkText(lua_State *L) {
+ // Der erste Parameter muss vom Typ userdata sein und die Metatable der Klasse Gfx.Text
+ uint *userDataPtr;
+ if ((userDataPtr = (uint *)my_checkudata(L, 1, TEXT_CLASS_NAME)) != 0) {
+ RenderObjectPtr<RenderObject> roPtr(*userDataPtr);
+ if (roPtr.isValid())
+ return roPtr->toText();
+ else
+ luaL_error(L, "The text with the handle %d does no longer exist.", *userDataPtr);
+ } else {
+ luaL_argcheck(L, 0, 1, "'" TEXT_CLASS_NAME "' expected");
+ }
+
+ return RenderObjectPtr<Text>();
+}
+
+static int t_setFont(lua_State *L) {
+ RenderObjectPtr<Text> textPtr = checkText(L);
+ BS_ASSERT(textPtr.isValid());
+ textPtr->setFont(luaL_checkstring(L, 2));
+ return 0;
+}
+
+static int t_setText(lua_State *L) {
+ RenderObjectPtr<Text> textPtr = checkText(L);
+ BS_ASSERT(textPtr.isValid());
+ textPtr->setText(luaL_checkstring(L, 2));
+ return 0;
+}
+
+static int t_setAlpha(lua_State *L) {
+ RenderObjectPtr<Text> textPtr = checkText(L);
+ BS_ASSERT(textPtr.isValid());
+ textPtr->setAlpha(static_cast<int>(luaL_checknumber(L, 2)));
+ return 0;
+}
+
+static int t_setColor(lua_State *L) {
+ RenderObjectPtr<Text> textPtr = checkText(L);
+ BS_ASSERT(textPtr.isValid());
+ textPtr->setColor(GraphicEngine::luaColorToARGBColor(L, 2));
+ return 0;
+}
+
+static int t_setAutoWrap(lua_State *L) {
+ RenderObjectPtr<Text> textPtr = checkText(L);
+ BS_ASSERT(textPtr.isValid());
+ textPtr->setAutoWrap(lua_tobooleancpp(L, 2));
+ return 0;
+}
+
+static int t_setAutoWrapThreshold(lua_State *L) {
+ RenderObjectPtr<Text> textPtr = checkText(L);
+ BS_ASSERT(textPtr.isValid());
+ textPtr->setAutoWrapThreshold(static_cast<uint>(luaL_checknumber(L, 2)));
+ return 0;
+}
+
+static int t_getText(lua_State *L) {
+ RenderObjectPtr<Text> textPtr = checkText(L);
+ BS_ASSERT(textPtr.isValid());
+ lua_pushstring(L, textPtr->getText().c_str());
+ return 1;
+}
+
+static int t_getFont(lua_State *L) {
+ RenderObjectPtr<Text> textPtr = checkText(L);
+ BS_ASSERT(textPtr.isValid());
+ lua_pushstring(L, textPtr->getFont().c_str());
+ return 1;
+}
+
+static int t_getAlpha(lua_State *L) {
+ RenderObjectPtr<Text> textPtr = checkText(L);
+ BS_ASSERT(textPtr.isValid());
+ lua_pushnumber(L, textPtr->getAlpha());
+ return 1;
+}
+
+static int t_getColor(lua_State *L) {
+ RenderObjectPtr<Text> textPtr = checkText(L);
+ BS_ASSERT(textPtr.isValid());
+ lua_pushnumber(L, textPtr->getColor());
+ return 1;
+}
+
+static int t_isAutoWrap(lua_State *L) {
+ RenderObjectPtr<Text> textPtr = checkText(L);
+ BS_ASSERT(textPtr.isValid());
+ lua_pushbooleancpp(L, textPtr->isAutoWrapActive());
+ return 1;
+}
+
+static int t_getAutoWrapThreshold(lua_State *L) {
+ RenderObjectPtr<Text> textPtr = checkText(L);
+ BS_ASSERT(textPtr.isValid());
+ lua_pushnumber(L, textPtr->getAutoWrapThreshold());
+ return 1;
+}
+
+static int t_remove(lua_State *L) {
+ RenderObjectPtr<Text> textPtr = checkText(L);
+ BS_ASSERT(textPtr.isValid());
+ textPtr.erase();
+ return 0;
+}
+
+static const luaL_reg TEXT_METHODS[] = {
+ {"SetFont", t_setFont},
+ {"SetText", t_setText},
+ {"SetAlpha", t_setAlpha},
+ {"SetColor", t_setColor},
+ {"SetAutoWrap", t_setAutoWrap},
+ {"SetAutoWrapThreshold", t_setAutoWrapThreshold},
+ {"GetText", t_getText},
+ {"GetFont", t_getFont},
+ {"GetAlpha", t_getAlpha},
+ {"GetColor", t_getColor},
+ {"IsAutoWrap", t_isAutoWrap},
+ {"GetAutoWrapThreshold", t_getAutoWrapThreshold},
+ {"Remove", t_remove},
+ {0, 0}
+};
+
+bool GraphicEngine::registerScriptBindings() {
+ Kernel *pKernel = Kernel::getInstance();
+ BS_ASSERT(pKernel);
+ ScriptEngine *pScript = pKernel->getScript();
+ BS_ASSERT(pScript);
+ lua_State *L = static_cast<lua_State *>(pScript->getScriptObject());
+ BS_ASSERT(L);
+
+ if (!LuaBindhelper::addMethodsToClass(L, BITMAP_CLASS_NAME, RENDEROBJECT_METHODS)) return false;
+ if (!LuaBindhelper::addMethodsToClass(L, ANIMATION_CLASS_NAME, RENDEROBJECT_METHODS)) return false;
+ if (!LuaBindhelper::addMethodsToClass(L, PANEL_CLASS_NAME, RENDEROBJECT_METHODS)) return false;
+ if (!LuaBindhelper::addMethodsToClass(L, TEXT_CLASS_NAME, RENDEROBJECT_METHODS)) return false;
+
+ if (!LuaBindhelper::addMethodsToClass(L, PANEL_CLASS_NAME, PANEL_METHODS)) return false;
+ if (!LuaBindhelper::addMethodsToClass(L, BITMAP_CLASS_NAME, BITMAP_METHODS)) return false;
+ if (!LuaBindhelper::addMethodsToClass(L, TEXT_CLASS_NAME, TEXT_METHODS)) return false;
+ if (!LuaBindhelper::addMethodsToClass(L, ANIMATION_CLASS_NAME, ANIMATION_METHODS)) return false;
+
+ if (!LuaBindhelper::addMethodsToClass(L, ANIMATION_TEMPLATE_CLASS_NAME, ANIMATION_TEMPLATE_METHODS)) return false;
+
+ if (!LuaBindhelper::addFunctionsToLib(L, GFX_LIBRARY_NAME, GFX_FUNCTIONS)) return false;
+
+ assert(loopPointCallbackPtr == 0);
+ loopPointCallbackPtr = new LuaCallback(L);
+
+ assert(actionCallbackPtr == 0);
+ actionCallbackPtr = new ActionCallback(L);
+
+ return true;
+}
+
+void GraphicEngine::unregisterScriptBindings() {
+ delete loopPointCallbackPtr;
+ loopPointCallbackPtr = 0;
+
+ delete actionCallbackPtr;
+ actionCallbackPtr = 0;
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/gfx/image/art.cpp b/engines/sword25/gfx/image/art.cpp
new file mode 100644
index 0000000000..064ca333e7
--- /dev/null
+++ b/engines/sword25/gfx/image/art.cpp
@@ -0,0 +1,2653 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * This code is based on Libart_LGPL - library of basic graphic primitives
+ *
+ * Copyright (c) 1998 Raph Levien
+ *
+ * Licensed under GNU LGPL v2
+ *
+ */
+
+/* Various utility functions RLL finds useful. */
+
+#include "sword25/gfx/image/art.h"
+
+namespace Sword25 {
+
+/**
+ * art_die: Print the error message to stderr and exit with a return code of 1.
+ * @fmt: The printf-style format for the error message.
+ *
+ * Used for dealing with severe errors.
+ **/
+void art_die(const char *fmt, ...) {
+ va_list ap;
+
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ exit(1);
+}
+
+/**
+ * art_warn: Print the warning message to stderr.
+ * @fmt: The printf-style format for the warning message.
+ *
+ * Used for generating warnings.
+ **/
+void art_warn(const char *fmt, ...) {
+ va_list ap;
+
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+}
+
+/**
+ * art_svp_free: Free an #ArtSVP structure.
+ * @svp: #ArtSVP to free.
+ *
+ * Frees an #ArtSVP structure and all the segments in it.
+ **/
+void art_svp_free(ArtSVP *svp) {
+ int n_segs = svp->n_segs;
+ int i;
+
+ for (i = 0; i < n_segs; i++)
+ free(svp->segs[i].points);
+ free(svp);
+}
+
+#define EPSILON 0
+
+/**
+ * art_svp_seg_compare: Compare two segments of an svp.
+ * @seg1: First segment to compare.
+ * @seg2: Second segment to compare.
+ *
+ * Compares two segments of an svp. Return 1 if @seg2 is below or to the
+ * right of @seg1, -1 otherwise.
+ **/
+int art_svp_seg_compare(const void *s1, const void *s2) {
+ const ArtSVPSeg *seg1 = (const ArtSVPSeg *)s1;
+ const ArtSVPSeg *seg2 = (const ArtSVPSeg *)s2;
+
+ if (seg1->points[0].y - EPSILON > seg2->points[0].y) return 1;
+ else if (seg1->points[0].y + EPSILON < seg2->points[0].y) return -1;
+ else if (seg1->points[0].x - EPSILON > seg2->points[0].x) return 1;
+ else if (seg1->points[0].x + EPSILON < seg2->points[0].x) return -1;
+ else if ((seg1->points[1].x - seg1->points[0].x) *
+ (seg2->points[1].y - seg2->points[0].y) -
+ (seg1->points[1].y - seg1->points[0].y) *
+ (seg2->points[1].x - seg2->points[0].x) > 0) return 1;
+ else return -1;
+}
+
+/**
+ * art_vpath_add_point: Add point to vpath.
+ * @p_vpath: Where the pointer to the #ArtVpath structure is stored.
+ * @pn_points: Pointer to the number of points in *@p_vpath.
+ * @pn_points_max: Pointer to the number of points allocated.
+ * @code: The pathcode for the new point.
+ * @x: The X coordinate of the new point.
+ * @y: The Y coordinate of the new point.
+ *
+ * Adds a new point to *@p_vpath, reallocating and updating *@p_vpath
+ * and *@pn_points_max as necessary. *@pn_points is incremented.
+ *
+ * This routine always adds the point after all points already in the
+ * vpath. Thus, it should be called in the order the points are
+ * desired.
+ **/
+void art_vpath_add_point(ArtVpath **p_vpath, int *pn_points, int *pn_points_max,
+ ArtPathcode code, double x, double y) {
+ int i;
+
+ i = (*pn_points)++;
+ if (i == *pn_points_max)
+ art_expand(*p_vpath, ArtVpath, *pn_points_max);
+ (*p_vpath)[i].code = code;
+ (*p_vpath)[i].x = x;
+ (*p_vpath)[i].y = y;
+}
+
+/* Sort vector paths into sorted vector paths */
+
+/* reverse a list of points in place */
+static void reverse_points(ArtPoint *points, int n_points) {
+ int i;
+ ArtPoint tmp_p;
+
+ for (i = 0; i < (n_points >> 1); i++) {
+ tmp_p = points[i];
+ points[i] = points[n_points - (i + 1)];
+ points[n_points - (i + 1)] = tmp_p;
+ }
+}
+
+/**
+ * art_svp_from_vpath: Convert a vpath to a sorted vector path.
+ * @vpath: #ArtVPath to convert.
+ *
+ * Converts a vector path into sorted vector path form. The svp form is
+ * more efficient for rendering and other vector operations.
+ *
+ * Basically, the implementation is to traverse the vector path,
+ * generating a new segment for each "run" of points in the vector
+ * path with monotonically increasing Y values. All the resulting
+ * values are then sorted.
+ *
+ * Note: I'm not sure that the sorting rule is correct with respect
+ * to numerical stability issues.
+ *
+ * Return value: Resulting sorted vector path.
+ **/
+ArtSVP *art_svp_from_vpath(ArtVpath *vpath) {
+ int n_segs, n_segs_max;
+ ArtSVP *svp;
+ int dir;
+ int new_dir;
+ int i;
+ ArtPoint *points;
+ int n_points, n_points_max;
+ double x, y;
+ double x_min, x_max;
+
+ n_segs = 0;
+ n_segs_max = 16;
+ svp = (ArtSVP *)malloc(sizeof(ArtSVP) +
+ (n_segs_max - 1) * sizeof(ArtSVPSeg));
+
+ dir = 0;
+ n_points = 0;
+ n_points_max = 0;
+ points = NULL;
+ i = 0;
+
+ x = y = 0; /* unnecessary, given "first code must not be LINETO" invariant,
+ but it makes gcc -Wall -ansi -pedantic happier */
+ x_min = x_max = 0; /* same */
+
+ while (vpath[i].code != ART_END) {
+ if (vpath[i].code == ART_MOVETO || vpath[i].code == ART_MOVETO_OPEN) {
+ if (points != NULL && n_points >= 2) {
+ if (n_segs == n_segs_max) {
+ n_segs_max <<= 1;
+ svp = (ArtSVP *)realloc(svp, sizeof(ArtSVP) +
+ (n_segs_max - 1) *
+ sizeof(ArtSVPSeg));
+ }
+ svp->segs[n_segs].n_points = n_points;
+ svp->segs[n_segs].dir = (dir > 0);
+ if (dir < 0)
+ reverse_points(points, n_points);
+ svp->segs[n_segs].points = points;
+ svp->segs[n_segs].bbox.x0 = x_min;
+ svp->segs[n_segs].bbox.x1 = x_max;
+ svp->segs[n_segs].bbox.y0 = points[0].y;
+ svp->segs[n_segs].bbox.y1 = points[n_points - 1].y;
+ n_segs++;
+ points = NULL;
+ }
+
+ if (points == NULL) {
+ n_points_max = 4;
+ points = art_new(ArtPoint, n_points_max);
+ }
+
+ n_points = 1;
+ points[0].x = x = vpath[i].x;
+ points[0].y = y = vpath[i].y;
+ x_min = x;
+ x_max = x;
+ dir = 0;
+ } else { /* must be LINETO */
+ new_dir = (vpath[i].y > y ||
+ (vpath[i].y == y && vpath[i].x > x)) ? 1 : -1;
+ if (dir && dir != new_dir) {
+ /* new segment */
+ x = points[n_points - 1].x;
+ y = points[n_points - 1].y;
+ if (n_segs == n_segs_max) {
+ n_segs_max <<= 1;
+ svp = (ArtSVP *)realloc(svp, sizeof(ArtSVP) +
+ (n_segs_max - 1) *
+ sizeof(ArtSVPSeg));
+ }
+ svp->segs[n_segs].n_points = n_points;
+ svp->segs[n_segs].dir = (dir > 0);
+ if (dir < 0)
+ reverse_points(points, n_points);
+ svp->segs[n_segs].points = points;
+ svp->segs[n_segs].bbox.x0 = x_min;
+ svp->segs[n_segs].bbox.x1 = x_max;
+ svp->segs[n_segs].bbox.y0 = points[0].y;
+ svp->segs[n_segs].bbox.y1 = points[n_points - 1].y;
+ n_segs++;
+
+ n_points = 1;
+ n_points_max = 4;
+ points = art_new(ArtPoint, n_points_max);
+ points[0].x = x;
+ points[0].y = y;
+ x_min = x;
+ x_max = x;
+ }
+
+ if (points != NULL) {
+ if (n_points == n_points_max)
+ art_expand(points, ArtPoint, n_points_max);
+ points[n_points].x = x = vpath[i].x;
+ points[n_points].y = y = vpath[i].y;
+ if (x < x_min) x_min = x;
+ else if (x > x_max) x_max = x;
+ n_points++;
+ }
+ dir = new_dir;
+ }
+ i++;
+ }
+
+ if (points != NULL) {
+ if (n_points >= 2) {
+ if (n_segs == n_segs_max) {
+ n_segs_max <<= 1;
+ svp = (ArtSVP *)realloc(svp, sizeof(ArtSVP) +
+ (n_segs_max - 1) *
+ sizeof(ArtSVPSeg));
+ }
+ svp->segs[n_segs].n_points = n_points;
+ svp->segs[n_segs].dir = (dir > 0);
+ if (dir < 0)
+ reverse_points(points, n_points);
+ svp->segs[n_segs].points = points;
+ svp->segs[n_segs].bbox.x0 = x_min;
+ svp->segs[n_segs].bbox.x1 = x_max;
+ svp->segs[n_segs].bbox.y0 = points[0].y;
+ svp->segs[n_segs].bbox.y1 = points[n_points - 1].y;
+ n_segs++;
+ } else
+ free(points);
+ }
+
+ svp->n_segs = n_segs;
+
+ qsort(&svp->segs, n_segs, sizeof(ArtSVPSeg), art_svp_seg_compare);
+
+ return svp;
+}
+
+
+/* Basic constructors and operations for bezier paths */
+
+#define RENDER_LEVEL 4
+#define RENDER_SIZE (1 << (RENDER_LEVEL))
+
+/**
+ * art_vpath_render_bez: Render a bezier segment into the vpath.
+ * @p_vpath: Where the pointer to the #ArtVpath structure is stored.
+ * @pn_points: Pointer to the number of points in *@p_vpath.
+ * @pn_points_max: Pointer to the number of points allocated.
+ * @x0: X coordinate of starting bezier point.
+ * @y0: Y coordinate of starting bezier point.
+ * @x1: X coordinate of first bezier control point.
+ * @y1: Y coordinate of first bezier control point.
+ * @x2: X coordinate of second bezier control point.
+ * @y2: Y coordinate of second bezier control point.
+ * @x3: X coordinate of ending bezier point.
+ * @y3: Y coordinate of ending bezier point.
+ * @flatness: Flatness control.
+ *
+ * Renders a bezier segment into the vector path, reallocating and
+ * updating *@p_vpath and *@pn_vpath_max as necessary. *@pn_vpath is
+ * incremented by the number of vector points added.
+ *
+ * This step includes (@x0, @y0) but not (@x3, @y3).
+ *
+ * The @flatness argument guides the amount of subdivision. The Adobe
+ * PostScript reference manual defines flatness as the maximum
+ * deviation between the any point on the vpath approximation and the
+ * corresponding point on the "true" curve, and we follow this
+ * definition here. A value of 0.25 should ensure high quality for aa
+ * rendering.
+**/
+static void art_vpath_render_bez(ArtVpath **p_vpath, int *pn, int *pn_max,
+ double x0, double y0,
+ double x1, double y1,
+ double x2, double y2,
+ double x3, double y3,
+ double flatness) {
+ double x3_0, y3_0;
+ double z3_0_dot;
+ double z1_dot, z2_dot;
+ double z1_perp, z2_perp;
+ double max_perp_sq;
+
+ double x_m, y_m;
+ double xa1, ya1;
+ double xa2, ya2;
+ double xb1, yb1;
+ double xb2, yb2;
+
+ /* It's possible to optimize this routine a fair amount.
+
+ First, once the _dot conditions are met, they will also be met in
+ all further subdivisions. So we might recurse to a different
+ routine that only checks the _perp conditions.
+
+ Second, the distance _should_ decrease according to fairly
+ predictable rules (a factor of 4 with each subdivision). So it might
+ be possible to note that the distance is within a factor of 4 of
+ acceptable, and subdivide once. But proving this might be hard.
+
+ Third, at the last subdivision, x_m and y_m can be computed more
+ expeditiously (as in the routine above).
+
+ Finally, if we were able to subdivide by, say 2 or 3, this would
+ allow considerably finer-grain control, i.e. fewer points for the
+ same flatness tolerance. This would speed things up downstream.
+
+ In any case, this routine is unlikely to be the bottleneck. It's
+ just that I have this undying quest for more speed...
+
+ */
+
+ x3_0 = x3 - x0;
+ y3_0 = y3 - y0;
+
+ /* z3_0_dot is dist z0-z3 squared */
+ z3_0_dot = x3_0 * x3_0 + y3_0 * y3_0;
+
+ if (z3_0_dot < 0.001) {
+ /* if start and end point are almost identical, the flatness tests
+ * don't work properly, so fall back on testing whether both of
+ * the other two control points are the same as the start point,
+ * too.
+ */
+ if (hypot(x1 - x0, y1 - y0) < 0.001
+ && hypot(x2 - x0, y2 - y0) < 0.001)
+ goto nosubdivide;
+ else
+ goto subdivide;
+ }
+
+ /* we can avoid subdivision if:
+
+ z1 has distance no more than flatness from the z0-z3 line
+
+ z1 is no more z0'ward than flatness past z0-z3
+
+ z1 is more z0'ward than z3'ward on the line traversing z0-z3
+
+ and correspondingly for z2 */
+
+ /* perp is distance from line, multiplied by dist z0-z3 */
+ max_perp_sq = flatness * flatness * z3_0_dot;
+
+ z1_perp = (y1 - y0) * x3_0 - (x1 - x0) * y3_0;
+ if (z1_perp * z1_perp > max_perp_sq)
+ goto subdivide;
+
+ z2_perp = (y3 - y2) * x3_0 - (x3 - x2) * y3_0;
+ if (z2_perp * z2_perp > max_perp_sq)
+ goto subdivide;
+
+ z1_dot = (x1 - x0) * x3_0 + (y1 - y0) * y3_0;
+ if (z1_dot < 0 && z1_dot * z1_dot > max_perp_sq)
+ goto subdivide;
+
+ z2_dot = (x3 - x2) * x3_0 + (y3 - y2) * y3_0;
+ if (z2_dot < 0 && z2_dot * z2_dot > max_perp_sq)
+ goto subdivide;
+
+ if (z1_dot + z1_dot > z3_0_dot)
+ goto subdivide;
+
+ if (z2_dot + z2_dot > z3_0_dot)
+ goto subdivide;
+
+
+nosubdivide:
+ /* don't subdivide */
+ art_vpath_add_point(p_vpath, pn, pn_max,
+ ART_LINETO, x3, y3);
+ return;
+
+subdivide:
+
+ xa1 = (x0 + x1) * 0.5;
+ ya1 = (y0 + y1) * 0.5;
+ xa2 = (x0 + 2 * x1 + x2) * 0.25;
+ ya2 = (y0 + 2 * y1 + y2) * 0.25;
+ xb1 = (x1 + 2 * x2 + x3) * 0.25;
+ yb1 = (y1 + 2 * y2 + y3) * 0.25;
+ xb2 = (x2 + x3) * 0.5;
+ yb2 = (y2 + y3) * 0.5;
+ x_m = (xa2 + xb1) * 0.5;
+ y_m = (ya2 + yb1) * 0.5;
+
+ art_vpath_render_bez(p_vpath, pn, pn_max,
+ x0, y0, xa1, ya1, xa2, ya2, x_m, y_m, flatness);
+ art_vpath_render_bez(p_vpath, pn, pn_max,
+ x_m, y_m, xb1, yb1, xb2, yb2, x3, y3, flatness);
+}
+
+/**
+ * art_bez_path_to_vec: Create vpath from bezier path.
+ * @bez: Bezier path.
+ * @flatness: Flatness control.
+ *
+ * Creates a vector path closely approximating the bezier path defined by
+ * @bez. The @flatness argument controls the amount of subdivision. In
+ * general, the resulting vpath deviates by at most @flatness pixels
+ * from the "ideal" path described by @bez.
+ *
+ * Return value: Newly allocated vpath.
+ **/
+ArtVpath *art_bez_path_to_vec(const ArtBpath *bez, double flatness) {
+ ArtVpath *vec;
+ int vec_n, vec_n_max;
+ int bez_index;
+ double x, y;
+
+ vec_n = 0;
+ vec_n_max = RENDER_SIZE;
+ vec = art_new(ArtVpath, vec_n_max);
+
+ /* Initialization is unnecessary because of the precondition that the
+ bezier path does not begin with LINETO or CURVETO, but is here
+ to make the code warning-free. */
+ x = 0;
+ y = 0;
+
+ bez_index = 0;
+ do {
+ /* make sure space for at least one more code */
+ if (vec_n >= vec_n_max)
+ art_expand(vec, ArtVpath, vec_n_max);
+ switch (bez[bez_index].code) {
+ case ART_MOVETO_OPEN:
+ case ART_MOVETO:
+ case ART_LINETO:
+ x = bez[bez_index].x3;
+ y = bez[bez_index].y3;
+ vec[vec_n].code = bez[bez_index].code;
+ vec[vec_n].x = x;
+ vec[vec_n].y = y;
+ vec_n++;
+ break;
+ case ART_END:
+ vec[vec_n].code = bez[bez_index].code;
+ vec[vec_n].x = 0;
+ vec[vec_n].y = 0;
+ vec_n++;
+ break;
+ case ART_CURVETO:
+ art_vpath_render_bez(&vec, &vec_n, &vec_n_max,
+ x, y,
+ bez[bez_index].x1, bez[bez_index].y1,
+ bez[bez_index].x2, bez[bez_index].y2,
+ bez[bez_index].x3, bez[bez_index].y3,
+ flatness);
+ x = bez[bez_index].x3;
+ y = bez[bez_index].y3;
+ break;
+ }
+ } while (bez[bez_index++].code != ART_END);
+ return vec;
+}
+
+
+#define EPSILON_6 1e-6
+#define EPSILON_2 1e-12
+
+/* Render an arc segment starting at (xc + x0, yc + y0) to (xc + x1,
+ yc + y1), centered at (xc, yc), and with given radius. Both x0^2 +
+ y0^2 and x1^2 + y1^2 should be equal to radius^2.
+
+ A positive value of radius means curve to the left, negative means
+ curve to the right.
+*/
+static void art_svp_vpath_stroke_arc(ArtVpath **p_vpath, int *pn, int *pn_max,
+ double xc, double yc,
+ double x0, double y0,
+ double x1, double y1,
+ double radius,
+ double flatness) {
+ double theta;
+ double th_0, th_1;
+ int n_pts;
+ int i;
+ double aradius;
+
+ aradius = fabs(radius);
+ theta = 2 * M_SQRT2 * sqrt(flatness / aradius);
+ th_0 = atan2(y0, x0);
+ th_1 = atan2(y1, x1);
+ if (radius > 0) {
+ /* curve to the left */
+ if (th_0 < th_1) th_0 += M_PI * 2;
+ n_pts = (int)ceil((th_0 - th_1) / theta);
+ } else {
+ /* curve to the right */
+ if (th_1 < th_0) th_1 += M_PI * 2;
+ n_pts = (int)ceil((th_1 - th_0) / theta);
+ }
+ art_vpath_add_point(p_vpath, pn, pn_max,
+ ART_LINETO, xc + x0, yc + y0);
+ for (i = 1; i < n_pts; i++) {
+ theta = th_0 + (th_1 - th_0) * i / n_pts;
+ art_vpath_add_point(p_vpath, pn, pn_max,
+ ART_LINETO, xc + cos(theta) * aradius,
+ yc + sin(theta) * aradius);
+ }
+ art_vpath_add_point(p_vpath, pn, pn_max,
+ ART_LINETO, xc + x1, yc + y1);
+}
+
+/* Assume that forw and rev are at point i0. Bring them to i1,
+ joining with the vector i1 - i2.
+
+ This used to be true, but isn't now that the stroke_raw code is
+ filtering out (near)zero length vectors: {It so happens that all
+ invocations of this function maintain the precondition i1 = i0 + 1,
+ so we could decrease the number of arguments by one. We haven't
+ done that here, though.}
+
+ forw is to the line's right and rev is to its left.
+
+ Precondition: no zero-length vectors, otherwise a divide by
+ zero will happen. */
+static void render_seg(ArtVpath **p_forw, int *pn_forw, int *pn_forw_max,
+ ArtVpath **p_rev, int *pn_rev, int *pn_rev_max,
+ ArtVpath *vpath, int i0, int i1, int i2,
+ ArtPathStrokeJoinType join,
+ double line_width, double miter_limit, double flatness) {
+ double dx0, dy0;
+ double dx1, dy1;
+ double dlx0, dly0;
+ double dlx1, dly1;
+ double dmx, dmy;
+ double dmr2;
+ double scale;
+ double cross;
+
+ /* The vectors of the lines from i0 to i1 and i1 to i2. */
+ dx0 = vpath[i1].x - vpath[i0].x;
+ dy0 = vpath[i1].y - vpath[i0].y;
+
+ dx1 = vpath[i2].x - vpath[i1].x;
+ dy1 = vpath[i2].y - vpath[i1].y;
+
+ /* Set dl[xy]0 to the vector from i0 to i1, rotated counterclockwise
+ 90 degrees, and scaled to the length of line_width. */
+ scale = line_width / sqrt(dx0 * dx0 + dy0 * dy0);
+ dlx0 = dy0 * scale;
+ dly0 = -dx0 * scale;
+
+ /* Set dl[xy]1 to the vector from i1 to i2, rotated counterclockwise
+ 90 degrees, and scaled to the length of line_width. */
+ scale = line_width / sqrt(dx1 * dx1 + dy1 * dy1);
+ dlx1 = dy1 * scale;
+ dly1 = -dx1 * scale;
+
+ /* now, forw's last point is expected to be colinear along d[xy]0
+ to point i0 - dl[xy]0, and rev with i0 + dl[xy]0. */
+
+ /* positive for positive area (i.e. left turn) */
+ cross = dx1 * dy0 - dx0 * dy1;
+
+ dmx = (dlx0 + dlx1) * 0.5;
+ dmy = (dly0 + dly1) * 0.5;
+ dmr2 = dmx * dmx + dmy * dmy;
+
+ if (join == ART_PATH_STROKE_JOIN_MITER &&
+ dmr2 * miter_limit * miter_limit < line_width * line_width)
+ join = ART_PATH_STROKE_JOIN_BEVEL;
+
+ /* the case when dmr2 is zero or very small bothers me
+ (i.e. near a 180 degree angle)
+ ALEX: So, we avoid the optimization when dmr2 is very small. This should
+ be safe since dmx/y is only used in optimization and in MITER case, and MITER
+ should be converted to BEVEL when dmr2 is very small. */
+ if (dmr2 > EPSILON_2) {
+ scale = line_width * line_width / dmr2;
+ dmx *= scale;
+ dmy *= scale;
+ }
+
+ if (cross *cross < EPSILON_2 && dx0 *dx1 + dy0 *dy1 >= 0) {
+ /* going straight */
+ art_vpath_add_point(p_forw, pn_forw, pn_forw_max,
+ ART_LINETO, vpath[i1].x - dlx0, vpath[i1].y - dly0);
+ art_vpath_add_point(p_rev, pn_rev, pn_rev_max,
+ ART_LINETO, vpath[i1].x + dlx0, vpath[i1].y + dly0);
+ } else if (cross > 0) {
+ /* left turn, forw is outside and rev is inside */
+
+ if (
+ (dmr2 > EPSILON_2) &&
+ /* check that i1 + dm[xy] is inside i0-i1 rectangle */
+ (dx0 + dmx) * dx0 + (dy0 + dmy) * dy0 > 0 &&
+ /* and that i1 + dm[xy] is inside i1-i2 rectangle */
+ ((dx1 - dmx) * dx1 + (dy1 - dmy) * dy1 > 0)
+#ifdef PEDANTIC_INNER
+ &&
+ /* check that i1 + dl[xy]1 is inside i0-i1 rectangle */
+ (dx0 + dlx1) * dx0 + (dy0 + dly1) * dy0 > 0 &&
+ /* and that i1 + dl[xy]0 is inside i1-i2 rectangle */
+ ((dx1 - dlx0) * dx1 + (dy1 - dly0) * dy1 > 0)
+#endif
+ ) {
+ /* can safely add single intersection point */
+ art_vpath_add_point(p_rev, pn_rev, pn_rev_max,
+ ART_LINETO, vpath[i1].x + dmx, vpath[i1].y + dmy);
+ } else {
+ /* need to loop-de-loop the inside */
+ art_vpath_add_point(p_rev, pn_rev, pn_rev_max,
+ ART_LINETO, vpath[i1].x + dlx0, vpath[i1].y + dly0);
+ art_vpath_add_point(p_rev, pn_rev, pn_rev_max,
+ ART_LINETO, vpath[i1].x, vpath[i1].y);
+ art_vpath_add_point(p_rev, pn_rev, pn_rev_max,
+ ART_LINETO, vpath[i1].x + dlx1, vpath[i1].y + dly1);
+ }
+
+ if (join == ART_PATH_STROKE_JOIN_BEVEL) {
+ /* bevel */
+ art_vpath_add_point(p_forw, pn_forw, pn_forw_max,
+ ART_LINETO, vpath[i1].x - dlx0, vpath[i1].y - dly0);
+ art_vpath_add_point(p_forw, pn_forw, pn_forw_max,
+ ART_LINETO, vpath[i1].x - dlx1, vpath[i1].y - dly1);
+ } else if (join == ART_PATH_STROKE_JOIN_MITER) {
+ art_vpath_add_point(p_forw, pn_forw, pn_forw_max,
+ ART_LINETO, vpath[i1].x - dmx, vpath[i1].y - dmy);
+ } else if (join == ART_PATH_STROKE_JOIN_ROUND)
+ art_svp_vpath_stroke_arc(p_forw, pn_forw, pn_forw_max,
+ vpath[i1].x, vpath[i1].y,
+ -dlx0, -dly0,
+ -dlx1, -dly1,
+ line_width,
+ flatness);
+ } else {
+ /* right turn, rev is outside and forw is inside */
+
+ if (
+ (dmr2 > EPSILON_2) &&
+ /* check that i1 - dm[xy] is inside i0-i1 rectangle */
+ (dx0 - dmx) * dx0 + (dy0 - dmy) * dy0 > 0 &&
+ /* and that i1 - dm[xy] is inside i1-i2 rectangle */
+ ((dx1 + dmx) * dx1 + (dy1 + dmy) * dy1 > 0)
+#ifdef PEDANTIC_INNER
+ &&
+ /* check that i1 - dl[xy]1 is inside i0-i1 rectangle */
+ (dx0 - dlx1) * dx0 + (dy0 - dly1) * dy0 > 0 &&
+ /* and that i1 - dl[xy]0 is inside i1-i2 rectangle */
+ ((dx1 + dlx0) * dx1 + (dy1 + dly0) * dy1 > 0)
+#endif
+ ) {
+ /* can safely add single intersection point */
+ art_vpath_add_point(p_forw, pn_forw, pn_forw_max,
+ ART_LINETO, vpath[i1].x - dmx, vpath[i1].y - dmy);
+ } else {
+ /* need to loop-de-loop the inside */
+ art_vpath_add_point(p_forw, pn_forw, pn_forw_max,
+ ART_LINETO, vpath[i1].x - dlx0, vpath[i1].y - dly0);
+ art_vpath_add_point(p_forw, pn_forw, pn_forw_max,
+ ART_LINETO, vpath[i1].x, vpath[i1].y);
+ art_vpath_add_point(p_forw, pn_forw, pn_forw_max,
+ ART_LINETO, vpath[i1].x - dlx1, vpath[i1].y - dly1);
+ }
+
+ if (join == ART_PATH_STROKE_JOIN_BEVEL) {
+ /* bevel */
+ art_vpath_add_point(p_rev, pn_rev, pn_rev_max,
+ ART_LINETO, vpath[i1].x + dlx0, vpath[i1].y + dly0);
+ art_vpath_add_point(p_rev, pn_rev, pn_rev_max,
+ ART_LINETO, vpath[i1].x + dlx1, vpath[i1].y + dly1);
+ } else if (join == ART_PATH_STROKE_JOIN_MITER) {
+ art_vpath_add_point(p_rev, pn_rev, pn_rev_max,
+ ART_LINETO, vpath[i1].x + dmx, vpath[i1].y + dmy);
+ } else if (join == ART_PATH_STROKE_JOIN_ROUND)
+ art_svp_vpath_stroke_arc(p_rev, pn_rev, pn_rev_max,
+ vpath[i1].x, vpath[i1].y,
+ dlx0, dly0,
+ dlx1, dly1,
+ -line_width,
+ flatness);
+
+ }
+}
+
+/* caps i1, under the assumption of a vector from i0 */
+static void render_cap(ArtVpath **p_result, int *pn_result, int *pn_result_max,
+ ArtVpath *vpath, int i0, int i1,
+ ArtPathStrokeCapType cap, double line_width, double flatness) {
+ double dx0, dy0;
+ double dlx0, dly0;
+ double scale;
+ int n_pts;
+ int i;
+
+ dx0 = vpath[i1].x - vpath[i0].x;
+ dy0 = vpath[i1].y - vpath[i0].y;
+
+ /* Set dl[xy]0 to the vector from i0 to i1, rotated counterclockwise
+ 90 degrees, and scaled to the length of line_width. */
+ scale = line_width / sqrt(dx0 * dx0 + dy0 * dy0);
+ dlx0 = dy0 * scale;
+ dly0 = -dx0 * scale;
+
+ switch (cap) {
+ case ART_PATH_STROKE_CAP_BUTT:
+ art_vpath_add_point(p_result, pn_result, pn_result_max,
+ ART_LINETO, vpath[i1].x - dlx0, vpath[i1].y - dly0);
+ art_vpath_add_point(p_result, pn_result, pn_result_max,
+ ART_LINETO, vpath[i1].x + dlx0, vpath[i1].y + dly0);
+ break;
+ case ART_PATH_STROKE_CAP_ROUND:
+ n_pts = (int)ceil(M_PI / (2.0 * M_SQRT2 * sqrt(flatness / line_width)));
+ art_vpath_add_point(p_result, pn_result, pn_result_max,
+ ART_LINETO, vpath[i1].x - dlx0, vpath[i1].y - dly0);
+ for (i = 1; i < n_pts; i++) {
+ double theta, c_th, s_th;
+
+ theta = M_PI * i / n_pts;
+ c_th = cos(theta);
+ s_th = sin(theta);
+ art_vpath_add_point(p_result, pn_result, pn_result_max,
+ ART_LINETO,
+ vpath[i1].x - dlx0 * c_th - dly0 * s_th,
+ vpath[i1].y - dly0 * c_th + dlx0 * s_th);
+ }
+ art_vpath_add_point(p_result, pn_result, pn_result_max,
+ ART_LINETO, vpath[i1].x + dlx0, vpath[i1].y + dly0);
+ break;
+ case ART_PATH_STROKE_CAP_SQUARE:
+ art_vpath_add_point(p_result, pn_result, pn_result_max,
+ ART_LINETO,
+ vpath[i1].x - dlx0 - dly0,
+ vpath[i1].y - dly0 + dlx0);
+ art_vpath_add_point(p_result, pn_result, pn_result_max,
+ ART_LINETO,
+ vpath[i1].x + dlx0 - dly0,
+ vpath[i1].y + dly0 + dlx0);
+ break;
+ }
+}
+
+/**
+ * art_svp_from_vpath_raw: Stroke a vector path, raw version
+ * @vpath: #ArtVPath to stroke.
+ * @join: Join style.
+ * @cap: Cap style.
+ * @line_width: Width of stroke.
+ * @miter_limit: Miter limit.
+ * @flatness: Flatness.
+ *
+ * Exactly the same as art_svp_vpath_stroke(), except that the resulting
+ * stroke outline may self-intersect and have regions of winding number
+ * greater than 1.
+ *
+ * Return value: Resulting raw stroked outline in svp format.
+ **/
+ArtVpath *art_svp_vpath_stroke_raw(ArtVpath *vpath,
+ ArtPathStrokeJoinType join,
+ ArtPathStrokeCapType cap,
+ double line_width,
+ double miter_limit,
+ double flatness) {
+ int begin_idx, end_idx;
+ int i;
+ ArtVpath *forw, *rev;
+ int n_forw, n_rev;
+ int n_forw_max, n_rev_max;
+ ArtVpath *result;
+ int n_result, n_result_max;
+ double half_lw = 0.5 * line_width;
+ int closed;
+ int last, this_, next, second;
+ double dx, dy;
+
+ n_forw_max = 16;
+ forw = art_new(ArtVpath, n_forw_max);
+
+ n_rev_max = 16;
+ rev = art_new(ArtVpath, n_rev_max);
+
+ n_result = 0;
+ n_result_max = 16;
+ result = art_new(ArtVpath, n_result_max);
+
+ for (begin_idx = 0; vpath[begin_idx].code != ART_END; begin_idx = end_idx) {
+ n_forw = 0;
+ n_rev = 0;
+
+ closed = (vpath[begin_idx].code == ART_MOVETO);
+
+ /* we don't know what the first point joins with until we get to the
+ last point and see if it's closed. So we start with the second
+ line in the path.
+
+ Note: this is not strictly true (we now know it's closed from
+ the opening pathcode), but why fix code that isn't broken?
+ */
+
+ this_ = begin_idx;
+ /* skip over identical points at the beginning of the subpath */
+ for (i = this_ + 1; vpath[i].code == ART_LINETO; i++) {
+ dx = vpath[i].x - vpath[this_].x;
+ dy = vpath[i].y - vpath[this_].y;
+ if (dx * dx + dy * dy > EPSILON_2)
+ break;
+ }
+ next = i;
+ second = next;
+
+ /* invariant: this doesn't coincide with next */
+ while (vpath[next].code == ART_LINETO) {
+ last = this_;
+ this_ = next;
+ /* skip over identical points after the beginning of the subpath */
+ for (i = this_ + 1; vpath[i].code == ART_LINETO; i++) {
+ dx = vpath[i].x - vpath[this_].x;
+ dy = vpath[i].y - vpath[this_].y;
+ if (dx * dx + dy * dy > EPSILON_2)
+ break;
+ }
+ next = i;
+ if (vpath[next].code != ART_LINETO) {
+ /* reached end of path */
+ /* make "closed" detection conform to PostScript
+ semantics (i.e. explicit closepath code rather than
+ just the fact that end of the path is the beginning) */
+ if (closed &&
+ vpath[this_].x == vpath[begin_idx].x &&
+ vpath[this_].y == vpath[begin_idx].y) {
+ int j;
+
+ /* path is closed, render join to beginning */
+ render_seg(&forw, &n_forw, &n_forw_max,
+ &rev, &n_rev, &n_rev_max,
+ vpath, last, this_, second,
+ join, half_lw, miter_limit, flatness);
+
+ /* do forward path */
+ art_vpath_add_point(&result, &n_result, &n_result_max,
+ ART_MOVETO, forw[n_forw - 1].x,
+ forw[n_forw - 1].y);
+ for (j = 0; j < n_forw; j++)
+ art_vpath_add_point(&result, &n_result, &n_result_max,
+ ART_LINETO, forw[j].x,
+ forw[j].y);
+
+ /* do reverse path, reversed */
+ art_vpath_add_point(&result, &n_result, &n_result_max,
+ ART_MOVETO, rev[0].x,
+ rev[0].y);
+ for (j = n_rev - 1; j >= 0; j--)
+ art_vpath_add_point(&result, &n_result, &n_result_max,
+ ART_LINETO, rev[j].x,
+ rev[j].y);
+ } else {
+ /* path is open */
+ int j;
+
+ /* add to forw rather than result to ensure that
+ forw has at least one point. */
+ render_cap(&forw, &n_forw, &n_forw_max,
+ vpath, last, this_,
+ cap, half_lw, flatness);
+ art_vpath_add_point(&result, &n_result, &n_result_max,
+ ART_MOVETO, forw[0].x,
+ forw[0].y);
+ for (j = 1; j < n_forw; j++)
+ art_vpath_add_point(&result, &n_result, &n_result_max,
+ ART_LINETO, forw[j].x,
+ forw[j].y);
+ for (j = n_rev - 1; j >= 0; j--)
+ art_vpath_add_point(&result, &n_result, &n_result_max,
+ ART_LINETO, rev[j].x,
+ rev[j].y);
+ render_cap(&result, &n_result, &n_result_max,
+ vpath, second, begin_idx,
+ cap, half_lw, flatness);
+ art_vpath_add_point(&result, &n_result, &n_result_max,
+ ART_LINETO, forw[0].x,
+ forw[0].y);
+ }
+ } else
+ render_seg(&forw, &n_forw, &n_forw_max,
+ &rev, &n_rev, &n_rev_max,
+ vpath, last, this_, next,
+ join, half_lw, miter_limit, flatness);
+ }
+ end_idx = next;
+ }
+
+ free(forw);
+ free(rev);
+ art_vpath_add_point(&result, &n_result, &n_result_max, ART_END, 0, 0);
+ return result;
+}
+
+
+/* Render a vector path into a stroked outline.
+
+ Status of this routine:
+
+ Basic correctness: Only miter and bevel line joins are implemented,
+ and only butt line caps. Otherwise, seems to be fine.
+
+ Numerical stability: We cheat (adding random perturbation). Thus,
+ it seems very likely that no numerical stability problems will be
+ seen in practice.
+
+ Speed: Should be pretty good.
+
+ Precision: The perturbation fuzzes the coordinates slightly,
+ but not enough to be visible. */
+
+/**
+ * art_svp_vpath_stroke: Stroke a vector path.
+ * @vpath: #ArtVPath to stroke.
+ * @join: Join style.
+ * @cap: Cap style.
+ * @line_width: Width of stroke.
+ * @miter_limit: Miter limit.
+ * @flatness: Flatness.
+ *
+ * Computes an svp representing the stroked outline of @vpath. The
+ * width of the stroked line is @line_width.
+ *
+ * Lines are joined according to the @join rule. Possible values are
+ * ART_PATH_STROKE_JOIN_MITER (for mitered joins),
+ * ART_PATH_STROKE_JOIN_ROUND (for round joins), and
+ * ART_PATH_STROKE_JOIN_BEVEL (for bevelled joins). The mitered join
+ * is converted to a bevelled join if the miter would extend to a
+ * distance of more than @miter_limit * @line_width from the actual
+ * join point.
+ *
+ * If there are open subpaths, the ends of these subpaths are capped
+ * according to the @cap rule. Possible values are
+ * ART_PATH_STROKE_CAP_BUTT (squared cap, extends exactly to end
+ * point), ART_PATH_STROKE_CAP_ROUND (rounded half-circle centered at
+ * the end point), and ART_PATH_STROKE_CAP_SQUARE (squared cap,
+ * extending half @line_width past the end point).
+ *
+ * The @flatness parameter controls the accuracy of the rendering. It
+ * is most important for determining the number of points to use to
+ * approximate circular arcs for round lines and joins. In general, the
+ * resulting vector path will be within @flatness pixels of the "ideal"
+ * path containing actual circular arcs. I reserve the right to use
+ * the @flatness parameter to convert bevelled joins to miters for very
+ * small turn angles, as this would reduce the number of points in the
+ * resulting outline path.
+ *
+ * The resulting path is "clean" with respect to self-intersections, i.e.
+ * the winding number is 0 or 1 at each point.
+ *
+ * Return value: Resulting stroked outline in svp format.
+ **/
+ArtSVP *art_svp_vpath_stroke(ArtVpath *vpath,
+ ArtPathStrokeJoinType join,
+ ArtPathStrokeCapType cap,
+ double line_width,
+ double miter_limit,
+ double flatness) {
+ ArtVpath *vpath_stroke;
+ ArtSVP *svp, *svp2;
+ ArtSvpWriter *swr;
+
+ vpath_stroke = art_svp_vpath_stroke_raw(vpath, join, cap,
+ line_width, miter_limit, flatness);
+ svp = art_svp_from_vpath(vpath_stroke);
+ free(vpath_stroke);
+
+ swr = art_svp_writer_rewind_new(ART_WIND_RULE_NONZERO);
+ art_svp_intersector(svp, swr);
+
+ svp2 = art_svp_writer_rewind_reap(swr);
+ art_svp_free(svp);
+ return svp2;
+}
+
+
+/* Testbed implementation of the new intersection code.
+*/
+
+typedef struct _ArtPriQ ArtPriQ;
+typedef struct _ArtPriPoint ArtPriPoint;
+
+struct _ArtPriQ {
+ int n_items;
+ int n_items_max;
+ ArtPriPoint **items;
+};
+
+struct _ArtPriPoint {
+ double x;
+ double y;
+ void *user_data;
+};
+
+static ArtPriQ *art_pri_new(void) {
+ ArtPriQ *result = art_new(ArtPriQ, 1);
+
+ result->n_items = 0;
+ result->n_items_max = 16;
+ result->items = art_new(ArtPriPoint *, result->n_items_max);
+ return result;
+}
+
+static void art_pri_free(ArtPriQ *pq) {
+ free(pq->items);
+ free(pq);
+}
+
+static art_boolean art_pri_empty(ArtPriQ *pq) {
+ return pq->n_items == 0;
+}
+
+/* This heap implementation is based on Vasek Chvatal's course notes:
+ http://www.cs.rutgers.edu/~chvatal/notes/pq.html#heap */
+
+static void art_pri_bubble_up(ArtPriQ *pq, int vacant, ArtPriPoint *missing) {
+ ArtPriPoint **items = pq->items;
+ int parent;
+
+ parent = (vacant - 1) >> 1;
+ while (vacant > 0 && (missing->y < items[parent]->y ||
+ (missing->y == items[parent]->y &&
+ missing->x < items[parent]->x))) {
+ items[vacant] = items[parent];
+ vacant = parent;
+ parent = (vacant - 1) >> 1;
+ }
+
+ items[vacant] = missing;
+}
+
+static void art_pri_insert(ArtPriQ *pq, ArtPriPoint *point) {
+ if (pq->n_items == pq->n_items_max)
+ art_expand(pq->items, ArtPriPoint *, pq->n_items_max);
+
+ art_pri_bubble_up(pq, pq->n_items++, point);
+}
+
+static void art_pri_sift_down_from_root(ArtPriQ *pq, ArtPriPoint *missing) {
+ ArtPriPoint **items = pq->items;
+ int vacant = 0, child = 2;
+ int n = pq->n_items;
+
+ while (child < n) {
+ if (items[child - 1]->y < items[child]->y ||
+ (items[child - 1]->y == items[child]->y &&
+ items[child - 1]->x < items[child]->x))
+ child--;
+ items[vacant] = items[child];
+ vacant = child;
+ child = (vacant + 1) << 1;
+ }
+ if (child == n) {
+ items[vacant] = items[n - 1];
+ vacant = n - 1;
+ }
+
+ art_pri_bubble_up(pq, vacant, missing);
+}
+
+static ArtPriPoint *art_pri_choose(ArtPriQ *pq) {
+ ArtPriPoint *result = pq->items[0];
+
+ art_pri_sift_down_from_root(pq, pq->items[--pq->n_items]);
+ return result;
+}
+
+/* A virtual class for an "svp writer". A client of this object creates an
+ SVP by repeatedly calling "add segment" and "add point" methods on it.
+*/
+
+typedef struct _ArtSvpWriterRewind ArtSvpWriterRewind;
+
+/* An implementation of the svp writer virtual class that applies the
+ winding rule. */
+
+struct _ArtSvpWriterRewind {
+ ArtSvpWriter super;
+ ArtWindRule rule;
+ ArtSVP *svp;
+ int n_segs_max;
+ int *n_points_max;
+};
+
+static int art_svp_writer_rewind_add_segment(ArtSvpWriter *self, int wind_left,
+ int delta_wind, double x, double y) {
+ ArtSvpWriterRewind *swr = (ArtSvpWriterRewind *)self;
+ ArtSVP *svp;
+ ArtSVPSeg *seg;
+ art_boolean left_filled = 0, right_filled = 0;
+ int wind_right = wind_left + delta_wind;
+ int seg_num;
+ const int init_n_points_max = 4;
+
+ switch (swr->rule) {
+ case ART_WIND_RULE_NONZERO:
+ left_filled = (wind_left != 0);
+ right_filled = (wind_right != 0);
+ break;
+ case ART_WIND_RULE_INTERSECT:
+ left_filled = (wind_left > 1);
+ right_filled = (wind_right > 1);
+ break;
+ case ART_WIND_RULE_ODDEVEN:
+ left_filled = (wind_left & 1);
+ right_filled = (wind_right & 1);
+ break;
+ case ART_WIND_RULE_POSITIVE:
+ left_filled = (wind_left > 0);
+ right_filled = (wind_right > 0);
+ break;
+ default:
+ art_die("Unknown wind rule %d\n", swr->rule);
+ }
+ if (left_filled == right_filled) {
+ /* discard segment now */
+ return -1;
+ }
+
+ svp = swr->svp;
+ seg_num = svp->n_segs++;
+ if (swr->n_segs_max == seg_num) {
+ swr->n_segs_max <<= 1;
+ svp = (ArtSVP *)realloc(svp, sizeof(ArtSVP) +
+ (swr->n_segs_max - 1) *
+ sizeof(ArtSVPSeg));
+ swr->svp = svp;
+ swr->n_points_max = art_renew(swr->n_points_max, int,
+ swr->n_segs_max);
+ }
+ seg = &svp->segs[seg_num];
+ seg->n_points = 1;
+ seg->dir = right_filled;
+ swr->n_points_max[seg_num] = init_n_points_max;
+ seg->bbox.x0 = x;
+ seg->bbox.y0 = y;
+ seg->bbox.x1 = x;
+ seg->bbox.y1 = y;
+ seg->points = art_new(ArtPoint, init_n_points_max);
+ seg->points[0].x = x;
+ seg->points[0].y = y;
+ return seg_num;
+}
+
+static void art_svp_writer_rewind_add_point(ArtSvpWriter *self, int seg_id,
+ double x, double y) {
+ ArtSvpWriterRewind *swr = (ArtSvpWriterRewind *)self;
+ ArtSVPSeg *seg;
+ int n_points;
+
+ if (seg_id < 0)
+ /* omitted segment */
+ return;
+
+ seg = &swr->svp->segs[seg_id];
+ n_points = seg->n_points++;
+ if (swr->n_points_max[seg_id] == n_points)
+ art_expand(seg->points, ArtPoint, swr->n_points_max[seg_id]);
+ seg->points[n_points].x = x;
+ seg->points[n_points].y = y;
+ if (x < seg->bbox.x0)
+ seg->bbox.x0 = x;
+ if (x > seg->bbox.x1)
+ seg->bbox.x1 = x;
+ seg->bbox.y1 = y;
+}
+
+static void art_svp_writer_rewind_close_segment(ArtSvpWriter *self, int seg_id) {
+ /* Not needed for this simple implementation. A potential future
+ optimization is to merge segments that can be merged safely. */
+}
+
+ArtSVP *art_svp_writer_rewind_reap(ArtSvpWriter *self) {
+ ArtSvpWriterRewind *swr = (ArtSvpWriterRewind *)self;
+ ArtSVP *result = swr->svp;
+
+ free(swr->n_points_max);
+ free(swr);
+ return result;
+}
+
+ArtSvpWriter *art_svp_writer_rewind_new(ArtWindRule rule) {
+ ArtSvpWriterRewind *result = art_new(ArtSvpWriterRewind, 1);
+
+ result->super.add_segment = art_svp_writer_rewind_add_segment;
+ result->super.add_point = art_svp_writer_rewind_add_point;
+ result->super.close_segment = art_svp_writer_rewind_close_segment;
+
+ result->rule = rule;
+ result->n_segs_max = 16;
+ result->svp = (ArtSVP *)malloc(sizeof(ArtSVP) +
+ (result->n_segs_max - 1) * sizeof(ArtSVPSeg));
+ result->svp->n_segs = 0;
+ result->n_points_max = art_new(int, result->n_segs_max);
+
+ return &result->super;
+}
+
+/* Now, data structures for the active list */
+
+typedef struct _ArtActiveSeg ArtActiveSeg;
+
+/* Note: BNEG is 1 for \ lines, and 0 for /. Thus,
+ x[(flags & BNEG) ^ 1] <= x[flags & BNEG] */
+#define ART_ACTIVE_FLAGS_BNEG 1
+
+/* This flag is set if the segment has been inserted into the active
+ list. */
+#define ART_ACTIVE_FLAGS_IN_ACTIVE 2
+
+/* This flag is set when the segment is to be deleted in the
+ horiz commit process. */
+#define ART_ACTIVE_FLAGS_DEL 4
+
+/* This flag is set if the seg_id is a valid output segment. */
+#define ART_ACTIVE_FLAGS_OUT 8
+
+/* This flag is set if the segment is in the horiz list. */
+#define ART_ACTIVE_FLAGS_IN_HORIZ 16
+
+struct _ArtActiveSeg {
+ int flags;
+ int wind_left, delta_wind;
+ ArtActiveSeg *left, *right; /* doubly linked list structure */
+
+ const ArtSVPSeg *in_seg;
+ int in_curs;
+
+ double x[2];
+ double y0, y1;
+ double a, b, c; /* line equation; ax+by+c = 0 for the line, a^2 + b^2 = 1,
+ and a>0 */
+
+ /* bottom point and intersection point stack */
+ int n_stack;
+ int n_stack_max;
+ ArtPoint *stack;
+
+ /* horiz commit list */
+ ArtActiveSeg *horiz_left, *horiz_right;
+ double horiz_x;
+ int horiz_delta_wind;
+ int seg_id;
+};
+
+typedef struct _ArtIntersectCtx ArtIntersectCtx;
+
+struct _ArtIntersectCtx {
+ const ArtSVP *in;
+ ArtSvpWriter *out;
+
+ ArtPriQ *pq;
+
+ ArtActiveSeg *active_head;
+
+ double y;
+ ArtActiveSeg *horiz_first;
+ ArtActiveSeg *horiz_last;
+
+ /* segment index of next input segment to be added to pri q */
+ int in_curs;
+};
+
+#define EPSILON_A 1e-5 /* Threshold for breaking lines at point insertions */
+
+/**
+ * art_svp_intersect_setup_seg: Set up an active segment from input segment.
+ * @seg: Active segment.
+ * @pri_pt: Priority queue point to initialize.
+ *
+ * Sets the x[], a, b, c, flags, and stack fields according to the
+ * line from the current cursor value. Sets the priority queue point
+ * to the bottom point of this line. Also advances the input segment
+ * cursor.
+ **/
+static void art_svp_intersect_setup_seg(ArtActiveSeg *seg, ArtPriPoint *pri_pt) {
+ const ArtSVPSeg *in_seg = seg->in_seg;
+ int in_curs = seg->in_curs++;
+ double x0, y0, x1, y1;
+ double dx, dy, s;
+ double a, b, r2;
+
+ x0 = in_seg->points[in_curs].x;
+ y0 = in_seg->points[in_curs].y;
+ x1 = in_seg->points[in_curs + 1].x;
+ y1 = in_seg->points[in_curs + 1].y;
+ pri_pt->x = x1;
+ pri_pt->y = y1;
+ dx = x1 - x0;
+ dy = y1 - y0;
+ r2 = dx * dx + dy * dy;
+ s = r2 == 0 ? 1 : 1 / sqrt(r2);
+ seg->a = a = dy * s;
+ seg->b = b = -dx * s;
+ seg->c = -(a * x0 + b * y0);
+ seg->flags = (seg->flags & ~ART_ACTIVE_FLAGS_BNEG) | (dx > 0);
+ seg->x[0] = x0;
+ seg->x[1] = x1;
+ seg->y0 = y0;
+ seg->y1 = y1;
+ seg->n_stack = 1;
+ seg->stack[0].x = x1;
+ seg->stack[0].y = y1;
+}
+
+/**
+ * art_svp_intersect_add_horiz: Add point to horizontal list.
+ * @ctx: Intersector context.
+ * @seg: Segment with point to insert into horizontal list.
+ *
+ * Inserts @seg into horizontal list, keeping it in ascending horiz_x
+ * order.
+ *
+ * Note: the horiz_commit routine processes "clusters" of segs in the
+ * horiz list, all sharing the same horiz_x value. The cluster is
+ * processed in active list order, rather than horiz list order. Thus,
+ * the order of segs in the horiz list sharing the same horiz_x
+ * _should_ be irrelevant. Even so, we use b as a secondary sorting key,
+ * as a "belt and suspenders" defensive coding tactic.
+ **/
+static void art_svp_intersect_add_horiz(ArtIntersectCtx *ctx, ArtActiveSeg *seg) {
+ ArtActiveSeg **pp = &ctx->horiz_last;
+ ArtActiveSeg *place;
+ ArtActiveSeg *place_right = NULL;
+
+ if (seg->flags & ART_ACTIVE_FLAGS_IN_HORIZ) {
+ art_warn("*** attempt to put segment in horiz list twice\n");
+ return;
+ }
+ seg->flags |= ART_ACTIVE_FLAGS_IN_HORIZ;
+
+ for (place = *pp; place != NULL && (place->horiz_x > seg->horiz_x ||
+ (place->horiz_x == seg->horiz_x &&
+ place->b < seg->b));
+ place = *pp) {
+ place_right = place;
+ pp = &place->horiz_left;
+ }
+ *pp = seg;
+ seg->horiz_left = place;
+ seg->horiz_right = place_right;
+ if (place == NULL)
+ ctx->horiz_first = seg;
+ else
+ place->horiz_right = seg;
+}
+
+static void art_svp_intersect_push_pt(ArtIntersectCtx *ctx, ArtActiveSeg *seg,
+ double x, double y) {
+ ArtPriPoint *pri_pt;
+ int n_stack = seg->n_stack;
+
+ if (n_stack == seg->n_stack_max)
+ art_expand(seg->stack, ArtPoint, seg->n_stack_max);
+ seg->stack[n_stack].x = x;
+ seg->stack[n_stack].y = y;
+ seg->n_stack++;
+
+ seg->x[1] = x;
+ seg->y1 = y;
+
+ pri_pt = art_new(ArtPriPoint, 1);
+ pri_pt->x = x;
+ pri_pt->y = y;
+ pri_pt->user_data = seg;
+ art_pri_insert(ctx->pq, pri_pt);
+}
+
+typedef enum {
+ ART_BREAK_LEFT = 1,
+ ART_BREAK_RIGHT = 2
+} ArtBreakFlags;
+
+/**
+ * art_svp_intersect_break: Break an active segment.
+ *
+ * Note: y must be greater than the top point's y, and less than
+ * the bottom's.
+ *
+ * Return value: x coordinate of break point.
+ */
+static double art_svp_intersect_break(ArtIntersectCtx *ctx, ArtActiveSeg *seg,
+ double x_ref, double y, ArtBreakFlags break_flags) {
+ double x0, y0, x1, y1;
+ const ArtSVPSeg *in_seg = seg->in_seg;
+ int in_curs = seg->in_curs;
+ double x;
+
+ x0 = in_seg->points[in_curs - 1].x;
+ y0 = in_seg->points[in_curs - 1].y;
+ x1 = in_seg->points[in_curs].x;
+ y1 = in_seg->points[in_curs].y;
+ x = x0 + (x1 - x0) * ((y - y0) / (y1 - y0));
+ if ((break_flags == ART_BREAK_LEFT && x > x_ref) ||
+ (break_flags == ART_BREAK_RIGHT && x < x_ref)) {
+ }
+
+ /* I think we can count on min(x0, x1) <= x <= max(x0, x1) with sane
+ arithmetic, but it might be worthwhile to check just in case. */
+
+ if (y > ctx->y)
+ art_svp_intersect_push_pt(ctx, seg, x, y);
+ else {
+ seg->x[0] = x;
+ seg->y0 = y;
+ seg->horiz_x = x;
+ art_svp_intersect_add_horiz(ctx, seg);
+ }
+
+ return x;
+}
+
+/**
+ * art_svp_intersect_add_point: Add a point, breaking nearby neighbors.
+ * @ctx: Intersector context.
+ * @x: X coordinate of point to add.
+ * @y: Y coordinate of point to add.
+ * @seg: "nearby" segment, or NULL if leftmost.
+ *
+ * Return value: Segment immediately to the left of the new point, or
+ * NULL if the new point is leftmost.
+ **/
+static ArtActiveSeg *art_svp_intersect_add_point(ArtIntersectCtx *ctx, double x, double y,
+ ArtActiveSeg *seg, ArtBreakFlags break_flags) {
+ ArtActiveSeg *left, *right;
+ double x_min = x, x_max = x;
+ art_boolean left_live, right_live;
+ double d;
+ double new_x;
+ ArtActiveSeg *test, *result = NULL;
+ double x_test;
+
+ left = seg;
+ if (left == NULL)
+ right = ctx->active_head;
+ else
+ right = left->right;
+ left_live = (break_flags & ART_BREAK_LEFT) && (left != NULL);
+ right_live = (break_flags & ART_BREAK_RIGHT) && (right != NULL);
+ while (left_live || right_live) {
+ if (left_live) {
+ if (x <= left->x[left->flags & ART_ACTIVE_FLAGS_BNEG] &&
+ /* It may be that one of these conjuncts turns out to be always
+ true. We test both anyway, to be defensive. */
+ y != left->y0 && y < left->y1) {
+ d = x_min * left->a + y * left->b + left->c;
+ if (d < EPSILON_A) {
+ new_x = art_svp_intersect_break(ctx, left, x_min, y,
+ ART_BREAK_LEFT);
+ if (new_x > x_max) {
+ x_max = new_x;
+ right_live = (right != NULL);
+ } else if (new_x < x_min)
+ x_min = new_x;
+ left = left->left;
+ left_live = (left != NULL);
+ } else
+ left_live = ART_FALSE;
+ } else
+ left_live = ART_FALSE;
+ } else if (right_live) {
+ if (x >= right->x[(right->flags & ART_ACTIVE_FLAGS_BNEG) ^ 1] &&
+ /* It may be that one of these conjuncts turns out to be always
+ true. We test both anyway, to be defensive. */
+ y != right->y0 && y < right->y1) {
+ d = x_max * right->a + y * right->b + right->c;
+ if (d > -EPSILON_A) {
+ new_x = art_svp_intersect_break(ctx, right, x_max, y,
+ ART_BREAK_RIGHT);
+ if (new_x < x_min) {
+ x_min = new_x;
+ left_live = (left != NULL);
+ } else if (new_x >= x_max)
+ x_max = new_x;
+ right = right->right;
+ right_live = (right != NULL);
+ } else
+ right_live = ART_FALSE;
+ } else
+ right_live = ART_FALSE;
+ }
+ }
+
+ /* Ascending order is guaranteed by break_flags. Thus, we don't need
+ to actually fix up non-ascending pairs. */
+
+ /* Now, (left, right) defines an interval of segments broken. Sort
+ into ascending x order. */
+ test = left == NULL ? ctx->active_head : left->right;
+ result = left;
+ if (test != NULL && test != right) {
+ if (y == test->y0)
+ x_test = test->x[0];
+ else /* assert y == test->y1, I think */
+ x_test = test->x[1];
+ for (;;) {
+ if (x_test <= x)
+ result = test;
+ test = test->right;
+ if (test == right)
+ break;
+ new_x = x_test;
+ if (new_x < x_test) {
+ art_warn("art_svp_intersect_add_point: non-ascending x\n");
+ }
+ x_test = new_x;
+ }
+ }
+ return result;
+}
+
+static void art_svp_intersect_swap_active(ArtIntersectCtx *ctx,
+ ArtActiveSeg *left_seg, ArtActiveSeg *right_seg) {
+ right_seg->left = left_seg->left;
+ if (right_seg->left != NULL)
+ right_seg->left->right = right_seg;
+ else
+ ctx->active_head = right_seg;
+ left_seg->right = right_seg->right;
+ if (left_seg->right != NULL)
+ left_seg->right->left = left_seg;
+ left_seg->left = right_seg;
+ right_seg->right = left_seg;
+}
+
+/**
+ * art_svp_intersect_test_cross: Test crossing of a pair of active segments.
+ * @ctx: Intersector context.
+ * @left_seg: Left segment of the pair.
+ * @right_seg: Right segment of the pair.
+ * @break_flags: Flags indicating whether to break neighbors.
+ *
+ * Tests crossing of @left_seg and @right_seg. If there is a crossing,
+ * inserts the intersection point into both segments.
+ *
+ * Return value: True if the intersection took place at the current
+ * scan line, indicating further iteration is needed.
+ **/
+static art_boolean art_svp_intersect_test_cross(ArtIntersectCtx *ctx,
+ ArtActiveSeg *left_seg, ArtActiveSeg *right_seg,
+ ArtBreakFlags break_flags) {
+ double left_x0, left_y0, left_x1;
+ double left_y1 = left_seg->y1;
+ double right_y1 = right_seg->y1;
+ double d;
+
+ const ArtSVPSeg *in_seg;
+ int in_curs;
+ double d0, d1, t;
+ double x, y; /* intersection point */
+
+ if (left_seg->y0 == right_seg->y0 && left_seg->x[0] == right_seg->x[0]) {
+ /* Top points of left and right segments coincide. This case
+ feels like a bit of duplication - we may want to merge it
+ with the cases below. However, this way, we're sure that this
+ logic makes only localized changes. */
+
+ if (left_y1 < right_y1) {
+ /* Test left (x1, y1) against right segment */
+ left_x1 = left_seg->x[1];
+
+ if (left_x1 <
+ right_seg->x[(right_seg->flags & ART_ACTIVE_FLAGS_BNEG) ^ 1] ||
+ left_y1 == right_seg->y0)
+ return ART_FALSE;
+ d = left_x1 * right_seg->a + left_y1 * right_seg->b + right_seg->c;
+ if (d < -EPSILON_A)
+ return ART_FALSE;
+ else if (d < EPSILON_A) {
+ /* I'm unsure about the break flags here. */
+ double right_x1 = art_svp_intersect_break(ctx, right_seg,
+ left_x1, left_y1,
+ ART_BREAK_RIGHT);
+ if (left_x1 <= right_x1)
+ return ART_FALSE;
+ }
+ } else if (left_y1 > right_y1) {
+ /* Test right (x1, y1) against left segment */
+ double right_x1 = right_seg->x[1];
+
+ if (right_x1 > left_seg->x[left_seg->flags & ART_ACTIVE_FLAGS_BNEG] ||
+ right_y1 == left_seg->y0)
+ return ART_FALSE;
+ d = right_x1 * left_seg->a + right_y1 * left_seg->b + left_seg->c;
+ if (d > EPSILON_A)
+ return ART_FALSE;
+ else if (d > -EPSILON_A) {
+ /* See above regarding break flags. */
+ left_x1 = art_svp_intersect_break(ctx, left_seg,
+ right_x1, right_y1,
+ ART_BREAK_LEFT);
+ if (left_x1 <= right_x1)
+ return ART_FALSE;
+ }
+ } else { /* left_y1 == right_y1 */
+ left_x1 = left_seg->x[1];
+ double right_x1 = right_seg->x[1];
+
+ if (left_x1 <= right_x1)
+ return ART_FALSE;
+ }
+ art_svp_intersect_swap_active(ctx, left_seg, right_seg);
+ return ART_TRUE;
+ }
+
+ if (left_y1 < right_y1) {
+ /* Test left (x1, y1) against right segment */
+ left_x1 = left_seg->x[1];
+
+ if (left_x1 <
+ right_seg->x[(right_seg->flags & ART_ACTIVE_FLAGS_BNEG) ^ 1] ||
+ left_y1 == right_seg->y0)
+ return ART_FALSE;
+ d = left_x1 * right_seg->a + left_y1 * right_seg->b + right_seg->c;
+ if (d < -EPSILON_A)
+ return ART_FALSE;
+ else if (d < EPSILON_A) {
+ double right_x1 = art_svp_intersect_break(ctx, right_seg,
+ left_x1, left_y1,
+ ART_BREAK_RIGHT);
+ if (left_x1 <= right_x1)
+ return ART_FALSE;
+ }
+ } else if (left_y1 > right_y1) {
+ /* Test right (x1, y1) against left segment */
+ double right_x1 = right_seg->x[1];
+
+ if (right_x1 > left_seg->x[left_seg->flags & ART_ACTIVE_FLAGS_BNEG] ||
+ right_y1 == left_seg->y0)
+ return ART_FALSE;
+ d = right_x1 * left_seg->a + right_y1 * left_seg->b + left_seg->c;
+ if (d > EPSILON_A)
+ return ART_FALSE;
+ else if (d > -EPSILON_A) {
+ left_x1 = art_svp_intersect_break(ctx, left_seg,
+ right_x1, right_y1,
+ ART_BREAK_LEFT);
+ if (left_x1 <= right_x1)
+ return ART_FALSE;
+ }
+ } else { /* left_y1 == right_y1 */
+ left_x1 = left_seg->x[1];
+ double right_x1 = right_seg->x[1];
+
+ if (left_x1 <= right_x1)
+ return ART_FALSE;
+ }
+
+ /* The segments cross. Find the intersection point. */
+
+ in_seg = left_seg->in_seg;
+ in_curs = left_seg->in_curs;
+ left_x0 = in_seg->points[in_curs - 1].x;
+ left_y0 = in_seg->points[in_curs - 1].y;
+ left_x1 = in_seg->points[in_curs].x;
+ left_y1 = in_seg->points[in_curs].y;
+ d0 = left_x0 * right_seg->a + left_y0 * right_seg->b + right_seg->c;
+ d1 = left_x1 * right_seg->a + left_y1 * right_seg->b + right_seg->c;
+ if (d0 == d1) {
+ x = left_x0;
+ y = left_y0;
+ } else {
+ /* Is this division always safe? It could possibly overflow. */
+ t = d0 / (d0 - d1);
+ if (t <= 0) {
+ x = left_x0;
+ y = left_y0;
+ } else if (t >= 1) {
+ x = left_x1;
+ y = left_y1;
+ } else {
+ x = left_x0 + t * (left_x1 - left_x0);
+ y = left_y0 + t * (left_y1 - left_y0);
+ }
+ }
+
+ /* Make sure intersection point is within bounds of right seg. */
+ if (y < right_seg->y0) {
+ x = right_seg->x[0];
+ y = right_seg->y0;
+ } else if (y > right_seg->y1) {
+ x = right_seg->x[1];
+ y = right_seg->y1;
+ } else if (x < right_seg->x[(right_seg->flags & ART_ACTIVE_FLAGS_BNEG) ^ 1])
+ x = right_seg->x[(right_seg->flags & ART_ACTIVE_FLAGS_BNEG) ^ 1];
+ else if (x > right_seg->x[right_seg->flags & ART_ACTIVE_FLAGS_BNEG])
+ x = right_seg->x[right_seg->flags & ART_ACTIVE_FLAGS_BNEG];
+
+ if (y == left_seg->y0) {
+ if (y != right_seg->y0) {
+ art_svp_intersect_push_pt(ctx, right_seg, x, y);
+ if ((break_flags & ART_BREAK_RIGHT) && right_seg->right != NULL)
+ art_svp_intersect_add_point(ctx, x, y, right_seg->right,
+ break_flags);
+ } else {
+ /* Intersection takes place at current scan line; process
+ immediately rather than queueing intersection point into
+ priq. */
+ ArtActiveSeg *winner, *loser;
+
+ /* Choose "most vertical" segement */
+ if (left_seg->a > right_seg->a) {
+ winner = left_seg;
+ loser = right_seg;
+ } else {
+ winner = right_seg;
+ loser = left_seg;
+ }
+
+ loser->x[0] = winner->x[0];
+ loser->horiz_x = loser->x[0];
+ loser->horiz_delta_wind += loser->delta_wind;
+ winner->horiz_delta_wind -= loser->delta_wind;
+
+ art_svp_intersect_swap_active(ctx, left_seg, right_seg);
+ return ART_TRUE;
+ }
+ } else if (y == right_seg->y0) {
+ art_svp_intersect_push_pt(ctx, left_seg, x, y);
+ if ((break_flags & ART_BREAK_LEFT) && left_seg->left != NULL)
+ art_svp_intersect_add_point(ctx, x, y, left_seg->left,
+ break_flags);
+ } else {
+ /* Insert the intersection point into both segments. */
+ art_svp_intersect_push_pt(ctx, left_seg, x, y);
+ art_svp_intersect_push_pt(ctx, right_seg, x, y);
+ if ((break_flags & ART_BREAK_LEFT) && left_seg->left != NULL)
+ art_svp_intersect_add_point(ctx, x, y, left_seg->left, break_flags);
+ if ((break_flags & ART_BREAK_RIGHT) && right_seg->right != NULL)
+ art_svp_intersect_add_point(ctx, x, y, right_seg->right, break_flags);
+ }
+ return ART_FALSE;
+}
+
+/**
+ * art_svp_intersect_active_delete: Delete segment from active list.
+ * @ctx: Intersection context.
+ * @seg: Segment to delete.
+ *
+ * Deletes @seg from the active list.
+ **/
+static void art_svp_intersect_active_delete(ArtIntersectCtx *ctx, ArtActiveSeg *seg) {
+ ArtActiveSeg *left = seg->left, *right = seg->right;
+
+ if (left != NULL)
+ left->right = right;
+ else
+ ctx->active_head = right;
+ if (right != NULL)
+ right->left = left;
+}
+
+/**
+ * art_svp_intersect_active_free: Free an active segment.
+ * @seg: Segment to delete.
+ *
+ * Frees @seg.
+ **/
+static void art_svp_intersect_active_free(ArtActiveSeg *seg) {
+ free(seg->stack);
+ free(seg);
+}
+
+/**
+ * art_svp_intersect_insert_cross: Test crossings of newly inserted line.
+ *
+ * Tests @seg against its left and right neighbors for intersections.
+ * Precondition: the line in @seg is not purely horizontal.
+ **/
+static void art_svp_intersect_insert_cross(ArtIntersectCtx *ctx,
+ ArtActiveSeg *seg) {
+ ArtActiveSeg *left = seg, *right = seg;
+
+ for (;;) {
+ if (left != NULL) {
+ ArtActiveSeg *leftc;
+
+ for (leftc = left->left; leftc != NULL; leftc = leftc->left)
+ if (!(leftc->flags & ART_ACTIVE_FLAGS_DEL))
+ break;
+ if (leftc != NULL &&
+ art_svp_intersect_test_cross(ctx, leftc, left,
+ ART_BREAK_LEFT)) {
+ if (left == right || right == NULL)
+ right = left->right;
+ } else {
+ left = NULL;
+ }
+ } else if (right != NULL && right->right != NULL) {
+ ArtActiveSeg *rightc;
+
+ for (rightc = right->right; rightc != NULL; rightc = rightc->right)
+ if (!(rightc->flags & ART_ACTIVE_FLAGS_DEL))
+ break;
+ if (rightc != NULL &&
+ art_svp_intersect_test_cross(ctx, right, rightc,
+ ART_BREAK_RIGHT)) {
+ if (left == right || left == NULL)
+ left = right->left;
+ } else {
+ right = NULL;
+ }
+ } else
+ break;
+ }
+}
+
+/**
+ * art_svp_intersect_horiz: Add horizontal line segment.
+ * @ctx: Intersector context.
+ * @seg: Segment on which to add horizontal line.
+ * @x0: Old x position.
+ * @x1: New x position.
+ *
+ * Adds a horizontal line from @x0 to @x1, and updates the current
+ * location of @seg to @x1.
+ **/
+static void art_svp_intersect_horiz(ArtIntersectCtx *ctx, ArtActiveSeg *seg,
+ double x0, double x1) {
+ ArtActiveSeg *hs;
+
+ if (x0 == x1)
+ return;
+
+ hs = art_new(ArtActiveSeg, 1);
+
+ hs->flags = ART_ACTIVE_FLAGS_DEL | (seg->flags & ART_ACTIVE_FLAGS_OUT);
+ if (seg->flags & ART_ACTIVE_FLAGS_OUT) {
+ ArtSvpWriter *swr = ctx->out;
+
+ swr->add_point(swr, seg->seg_id, x0, ctx->y);
+ }
+ hs->seg_id = seg->seg_id;
+ hs->horiz_x = x0;
+ hs->horiz_delta_wind = seg->delta_wind;
+ hs->stack = NULL;
+
+ /* Ideally, the (a, b, c) values will never be read. However, there
+ are probably some tests remaining that don't check for _DEL
+ before evaluating the line equation. For those, these
+ initializations will at least prevent a UMR of the values, which
+ can crash on some platforms. */
+ hs->a = 0.0;
+ hs->b = 0.0;
+ hs->c = 0.0;
+
+ seg->horiz_delta_wind -= seg->delta_wind;
+
+ art_svp_intersect_add_horiz(ctx, hs);
+
+ if (x0 > x1) {
+ ArtActiveSeg *left;
+ art_boolean first = ART_TRUE;
+
+ for (left = seg->left; left != NULL; left = seg->left) {
+ int left_bneg = left->flags & ART_ACTIVE_FLAGS_BNEG;
+
+ if (left->x[left_bneg] <= x1)
+ break;
+ if (left->x[left_bneg ^ 1] <= x1 &&
+ x1 *left->a + ctx->y *left->b + left->c >= 0)
+ break;
+ if (left->y0 != ctx->y && left->y1 != ctx->y) {
+ art_svp_intersect_break(ctx, left, x1, ctx->y, ART_BREAK_LEFT);
+ }
+ art_svp_intersect_swap_active(ctx, left, seg);
+ if (first && left->right != NULL) {
+ art_svp_intersect_test_cross(ctx, left, left->right,
+ ART_BREAK_RIGHT);
+ first = ART_FALSE;
+ }
+ }
+ } else {
+ ArtActiveSeg *right;
+ art_boolean first = ART_TRUE;
+
+ for (right = seg->right; right != NULL; right = seg->right) {
+ int right_bneg = right->flags & ART_ACTIVE_FLAGS_BNEG;
+
+ if (right->x[right_bneg ^ 1] >= x1)
+ break;
+ if (right->x[right_bneg] >= x1 &&
+ x1 *right->a + ctx->y *right->b + right->c <= 0)
+ break;
+ if (right->y0 != ctx->y && right->y1 != ctx->y) {
+ art_svp_intersect_break(ctx, right, x1, ctx->y,
+ ART_BREAK_LEFT);
+ }
+ art_svp_intersect_swap_active(ctx, seg, right);
+ if (first && right->left != NULL) {
+ art_svp_intersect_test_cross(ctx, right->left, right,
+ ART_BREAK_RIGHT);
+ first = ART_FALSE;
+ }
+ }
+ }
+
+ seg->x[0] = x1;
+ seg->x[1] = x1;
+ seg->horiz_x = x1;
+ seg->flags &= ~ART_ACTIVE_FLAGS_OUT;
+}
+
+/**
+ * art_svp_intersect_insert_line: Insert a line into the active list.
+ * @ctx: Intersector context.
+ * @seg: Segment containing line to insert.
+ *
+ * Inserts the line into the intersector context, taking care of any
+ * intersections, and adding the appropriate horizontal points to the
+ * active list.
+ **/
+static void art_svp_intersect_insert_line(ArtIntersectCtx *ctx, ArtActiveSeg *seg) {
+ if (seg->y1 == seg->y0) {
+ art_svp_intersect_horiz(ctx, seg, seg->x[0], seg->x[1]);
+ } else {
+ art_svp_intersect_insert_cross(ctx, seg);
+ art_svp_intersect_add_horiz(ctx, seg);
+ }
+}
+
+static void art_svp_intersect_process_intersection(ArtIntersectCtx *ctx,
+ ArtActiveSeg *seg) {
+ int n_stack = --seg->n_stack;
+ seg->x[1] = seg->stack[n_stack - 1].x;
+ seg->y1 = seg->stack[n_stack - 1].y;
+ seg->x[0] = seg->stack[n_stack].x;
+ seg->y0 = seg->stack[n_stack].y;
+ seg->horiz_x = seg->x[0];
+ art_svp_intersect_insert_line(ctx, seg);
+}
+
+static void art_svp_intersect_advance_cursor(ArtIntersectCtx *ctx, ArtActiveSeg *seg,
+ ArtPriPoint *pri_pt) {
+ const ArtSVPSeg *in_seg = seg->in_seg;
+ int in_curs = seg->in_curs;
+ ArtSvpWriter *swr = seg->flags & ART_ACTIVE_FLAGS_OUT ? ctx->out : NULL;
+
+ if (swr != NULL)
+ swr->add_point(swr, seg->seg_id, seg->x[1], seg->y1);
+ if (in_curs + 1 == in_seg->n_points) {
+ ArtActiveSeg *left = seg->left, *right = seg->right;
+
+ seg->flags |= ART_ACTIVE_FLAGS_DEL;
+ art_svp_intersect_add_horiz(ctx, seg);
+ art_svp_intersect_active_delete(ctx, seg);
+ if (left != NULL && right != NULL)
+ art_svp_intersect_test_cross(ctx, left, right,
+ (ArtBreakFlags)(ART_BREAK_LEFT | ART_BREAK_RIGHT));
+ free(pri_pt);
+ } else {
+ seg->horiz_x = seg->x[1];
+
+ art_svp_intersect_setup_seg(seg, pri_pt);
+ art_pri_insert(ctx->pq, pri_pt);
+ art_svp_intersect_insert_line(ctx, seg);
+ }
+}
+
+static void art_svp_intersect_add_seg(ArtIntersectCtx *ctx, const ArtSVPSeg *in_seg) {
+ ArtActiveSeg *seg = art_new(ArtActiveSeg, 1);
+ ArtActiveSeg *test;
+ double x0, y0;
+ ArtActiveSeg *beg_range;
+ ArtActiveSeg *last = NULL;
+ ArtActiveSeg *left, *right;
+ ArtPriPoint *pri_pt = art_new(ArtPriPoint, 1);
+
+ seg->flags = 0;
+ seg->in_seg = in_seg;
+ seg->in_curs = 0;
+
+ seg->n_stack_max = 4;
+ seg->stack = art_new(ArtPoint, seg->n_stack_max);
+
+ seg->horiz_delta_wind = 0;
+
+ seg->wind_left = 0;
+
+ pri_pt->user_data = seg;
+ art_svp_intersect_setup_seg(seg, pri_pt);
+ art_pri_insert(ctx->pq, pri_pt);
+
+ /* Find insertion place for new segment */
+ /* This is currently a left-to-right scan, but should be replaced
+ with a binary search as soon as it's validated. */
+
+ x0 = in_seg->points[0].x;
+ y0 = in_seg->points[0].y;
+ beg_range = NULL;
+ for (test = ctx->active_head; test != NULL; test = test->right) {
+ double d;
+ int test_bneg = test->flags & ART_ACTIVE_FLAGS_BNEG;
+
+ if (x0 < test->x[test_bneg]) {
+ if (x0 < test->x[test_bneg ^ 1])
+ break;
+ d = x0 * test->a + y0 * test->b + test->c;
+ if (d < 0)
+ break;
+ }
+ last = test;
+ }
+
+ left = art_svp_intersect_add_point(ctx, x0, y0, last, (ArtBreakFlags)(ART_BREAK_LEFT | ART_BREAK_RIGHT));
+ seg->left = left;
+ if (left == NULL) {
+ right = ctx->active_head;
+ ctx->active_head = seg;
+ } else {
+ right = left->right;
+ left->right = seg;
+ }
+ seg->right = right;
+ if (right != NULL)
+ right->left = seg;
+
+ seg->delta_wind = in_seg->dir ? 1 : -1;
+ seg->horiz_x = x0;
+
+ art_svp_intersect_insert_line(ctx, seg);
+}
+
+/**
+ * art_svp_intersect_horiz_commit: Commit points in horiz list to output.
+ * @ctx: Intersection context.
+ *
+ * The main function of the horizontal commit is to output new
+ * points to the output writer.
+ *
+ * This "commit" pass is also where winding numbers are assigned,
+ * because doing it here provides much greater tolerance for inputs
+ * which are not in strict SVP order.
+ *
+ * Each cluster in the horiz_list contains both segments that are in
+ * the active list (ART_ACTIVE_FLAGS_DEL is false) and that are not,
+ * and are scheduled to be deleted (ART_ACTIVE_FLAGS_DEL is true). We
+ * need to deal with both.
+ **/
+static void art_svp_intersect_horiz_commit(ArtIntersectCtx *ctx) {
+ ArtActiveSeg *seg;
+ int winding_number = 0; /* initialization just to avoid warning */
+ int horiz_wind = 0;
+ double last_x = 0; /* initialization just to avoid warning */
+
+ /* Output points to svp writer. */
+ for (seg = ctx->horiz_first; seg != NULL;) {
+ /* Find a cluster with common horiz_x, */
+ ArtActiveSeg *curs;
+ double x = seg->horiz_x;
+
+ /* Generate any horizontal segments. */
+ if (horiz_wind != 0) {
+ ArtSvpWriter *swr = ctx->out;
+ int seg_id;
+
+ seg_id = swr->add_segment(swr, winding_number, horiz_wind,
+ last_x, ctx->y);
+ swr->add_point(swr, seg_id, x, ctx->y);
+ swr->close_segment(swr, seg_id);
+ }
+
+ /* Find first active segment in cluster. */
+
+ for (curs = seg; curs != NULL && curs->horiz_x == x;
+ curs = curs->horiz_right)
+ if (!(curs->flags & ART_ACTIVE_FLAGS_DEL))
+ break;
+
+ if (curs != NULL && curs->horiz_x == x) {
+ /* There exists at least one active segment in this cluster. */
+
+ /* Find beginning of cluster. */
+ for (; curs->left != NULL; curs = curs->left)
+ if (curs->left->horiz_x != x)
+ break;
+
+ if (curs->left != NULL)
+ winding_number = curs->left->wind_left + curs->left->delta_wind;
+ else
+ winding_number = 0;
+
+ do {
+ if (!(curs->flags & ART_ACTIVE_FLAGS_OUT) ||
+ curs->wind_left != winding_number) {
+ ArtSvpWriter *swr = ctx->out;
+
+ if (curs->flags & ART_ACTIVE_FLAGS_OUT) {
+ swr->add_point(swr, curs->seg_id,
+ curs->horiz_x, ctx->y);
+ swr->close_segment(swr, curs->seg_id);
+ }
+
+ curs->seg_id = swr->add_segment(swr, winding_number,
+ curs->delta_wind,
+ x, ctx->y);
+ curs->flags |= ART_ACTIVE_FLAGS_OUT;
+ }
+ curs->wind_left = winding_number;
+ winding_number += curs->delta_wind;
+ curs = curs->right;
+ } while (curs != NULL && curs->horiz_x == x);
+ }
+
+ /* Skip past cluster. */
+ do {
+ ArtActiveSeg *next = seg->horiz_right;
+
+ seg->flags &= ~ART_ACTIVE_FLAGS_IN_HORIZ;
+ horiz_wind += seg->horiz_delta_wind;
+ seg->horiz_delta_wind = 0;
+ if (seg->flags & ART_ACTIVE_FLAGS_DEL) {
+ if (seg->flags & ART_ACTIVE_FLAGS_OUT) {
+ ArtSvpWriter *swr = ctx->out;
+ swr->close_segment(swr, seg->seg_id);
+ }
+ art_svp_intersect_active_free(seg);
+ }
+ seg = next;
+ } while (seg != NULL && seg->horiz_x == x);
+
+ last_x = x;
+ }
+ ctx->horiz_first = NULL;
+ ctx->horiz_last = NULL;
+}
+
+void art_svp_intersector(const ArtSVP *in, ArtSvpWriter *out) {
+ ArtIntersectCtx *ctx;
+ ArtPriQ *pq;
+ ArtPriPoint *first_point;
+
+ if (in->n_segs == 0)
+ return;
+
+ ctx = art_new(ArtIntersectCtx, 1);
+ ctx->in = in;
+ ctx->out = out;
+ pq = art_pri_new();
+ ctx->pq = pq;
+
+ ctx->active_head = NULL;
+
+ ctx->horiz_first = NULL;
+ ctx->horiz_last = NULL;
+
+ ctx->in_curs = 0;
+ first_point = art_new(ArtPriPoint, 1);
+ first_point->x = in->segs[0].points[0].x;
+ first_point->y = in->segs[0].points[0].y;
+ first_point->user_data = NULL;
+ ctx->y = first_point->y;
+ art_pri_insert(pq, first_point);
+
+ while (!art_pri_empty(pq)) {
+ ArtPriPoint *pri_point = art_pri_choose(pq);
+ ArtActiveSeg *seg = (ArtActiveSeg *)pri_point->user_data;
+
+ if (ctx->y != pri_point->y) {
+ art_svp_intersect_horiz_commit(ctx);
+ ctx->y = pri_point->y;
+ }
+
+ if (seg == NULL) {
+ /* Insert new segment from input */
+ const ArtSVPSeg *in_seg = &in->segs[ctx->in_curs++];
+ art_svp_intersect_add_seg(ctx, in_seg);
+ if (ctx->in_curs < in->n_segs) {
+ const ArtSVPSeg *next_seg = &in->segs[ctx->in_curs];
+ pri_point->x = next_seg->points[0].x;
+ pri_point->y = next_seg->points[0].y;
+ /* user_data is already NULL */
+ art_pri_insert(pq, pri_point);
+ } else
+ free(pri_point);
+ } else {
+ int n_stack = seg->n_stack;
+
+ if (n_stack > 1) {
+ art_svp_intersect_process_intersection(ctx, seg);
+ free(pri_point);
+ } else {
+ art_svp_intersect_advance_cursor(ctx, seg, pri_point);
+ }
+ }
+ }
+
+ art_svp_intersect_horiz_commit(ctx);
+
+ art_pri_free(pq);
+ free(ctx);
+}
+
+
+/* The spiffy antialiased renderer for sorted vector paths. */
+
+typedef double artfloat;
+
+struct _ArtSVPRenderAAIter {
+ const ArtSVP *svp;
+ int x0, x1;
+ int y;
+ int seg_ix;
+
+ int *active_segs;
+ int n_active_segs;
+ int *cursor;
+ artfloat *seg_x;
+ artfloat *seg_dx;
+
+ ArtSVPRenderAAStep *steps;
+};
+
+static void art_svp_render_insert_active(int i, int *active_segs, int n_active_segs,
+ artfloat *seg_x, artfloat *seg_dx) {
+ int j;
+ artfloat x;
+ int tmp1, tmp2;
+
+ /* this is a cheap hack to get ^'s sorted correctly */
+ x = seg_x[i] + 0.001 * seg_dx[i];
+ for (j = 0; j < n_active_segs && seg_x[active_segs[j]] < x; j++)
+ ;
+
+ tmp1 = i;
+ while (j < n_active_segs) {
+ tmp2 = active_segs[j];
+ active_segs[j] = tmp1;
+ tmp1 = tmp2;
+ j++;
+ }
+ active_segs[j] = tmp1;
+}
+
+static void art_svp_render_delete_active(int *active_segs, int j, int n_active_segs) {
+ int k;
+
+ for (k = j; k < n_active_segs; k++)
+ active_segs[k] = active_segs[k + 1];
+}
+
+/* Render the sorted vector path in the given rectangle, antialiased.
+
+ This interface uses a callback for the actual pixel rendering. The
+ callback is called y1 - y0 times (once for each scan line). The y
+ coordinate is given as an argument for convenience (it could be
+ stored in the callback's private data and incremented on each
+ call).
+
+ The rendered polygon is represented in a semi-runlength format: a
+ start value and a sequence of "steps". Each step has an x
+ coordinate and a value delta. The resulting value at position x is
+ equal to the sum of the start value and all step delta values for
+ which the step x coordinate is less than or equal to x. An
+ efficient algorithm will traverse the steps left to right, keeping
+ a running sum.
+
+ All x coordinates in the steps are guaranteed to be x0 <= x < x1.
+ (This guarantee is a change from the gfonted vpaar renderer, and is
+ designed to simplify the callback).
+
+ There is now a further guarantee that no two steps will have the
+ same x value. This may allow for further speedup and simplification
+ of renderers.
+
+ The value 0x8000 represents 0% coverage by the polygon, while
+ 0xff8000 represents 100% coverage. This format is designed so that
+ >> 16 results in a standard 0x00..0xff value range, with nice
+ rounding.
+
+ Status of this routine:
+
+ Basic correctness: OK
+
+ Numerical stability: pretty good, although probably not
+ bulletproof.
+
+ Speed: Needs more aggressive culling of bounding boxes. Can
+ probably speed up the [x0,x1) clipping of step values. Can do more
+ of the step calculation in fixed point.
+
+ Precision: No known problems, although it should be tested
+ thoroughly, especially for symmetry.
+
+*/
+
+ArtSVPRenderAAIter *art_svp_render_aa_iter(const ArtSVP *svp,
+ int x0, int y0, int x1, int y1) {
+ ArtSVPRenderAAIter *iter = art_new(ArtSVPRenderAAIter, 1);
+
+ iter->svp = svp;
+ iter->y = y0;
+ iter->x0 = x0;
+ iter->x1 = x1;
+ iter->seg_ix = 0;
+
+ iter->active_segs = art_new(int, svp->n_segs);
+ iter->cursor = art_new(int, svp->n_segs);
+ iter->seg_x = art_new(artfloat, svp->n_segs);
+ iter->seg_dx = art_new(artfloat, svp->n_segs);
+ iter->steps = art_new(ArtSVPRenderAAStep, x1 - x0);
+ iter->n_active_segs = 0;
+
+ return iter;
+}
+
+#define ADD_STEP(xpos, xdelta) \
+ /* stereotype code fragment for adding a step */ \
+ if (n_steps == 0 || steps[n_steps - 1].x < xpos) \
+ { \
+ sx = n_steps; \
+ steps[sx].x = xpos; \
+ steps[sx].delta = xdelta; \
+ n_steps++; \
+ } \
+ else \
+ { \
+ for (sx = n_steps; sx > 0; sx--) \
+ { \
+ if (steps[sx - 1].x == xpos) \
+ { \
+ steps[sx - 1].delta += xdelta; \
+ sx = n_steps; \
+ break; \
+ } \
+ else if (steps[sx - 1].x < xpos) \
+ { \
+ break; \
+ } \
+ } \
+ if (sx < n_steps) \
+ { \
+ memmove (&steps[sx + 1], &steps[sx], \
+ (n_steps - sx) * sizeof(steps[0])); \
+ steps[sx].x = xpos; \
+ steps[sx].delta = xdelta; \
+ n_steps++; \
+ } \
+ }
+
+void art_svp_render_aa_iter_step(ArtSVPRenderAAIter *iter, int *p_start,
+ ArtSVPRenderAAStep **p_steps, int *p_n_steps) {
+ const ArtSVP *svp = iter->svp;
+ int *active_segs = iter->active_segs;
+ int n_active_segs = iter->n_active_segs;
+ int *cursor = iter->cursor;
+ artfloat *seg_x = iter->seg_x;
+ artfloat *seg_dx = iter->seg_dx;
+ int i = iter->seg_ix;
+ int j;
+ int x0 = iter->x0;
+ int x1 = iter->x1;
+ int y = iter->y;
+ int seg_index;
+
+ int x;
+ ArtSVPRenderAAStep *steps = iter->steps;
+ int n_steps;
+ artfloat y_top, y_bot;
+ artfloat x_top, x_bot;
+ artfloat x_min, x_max;
+ int ix_min, ix_max;
+ artfloat delta; /* delta should be int too? */
+ int last, this_;
+ int xdelta;
+ artfloat rslope, drslope;
+ int start;
+ const ArtSVPSeg *seg;
+ int curs;
+ artfloat dy;
+
+ int sx;
+
+ /* insert new active segments */
+ for (; i < svp->n_segs && svp->segs[i].bbox.y0 < y + 1; i++) {
+ if (svp->segs[i].bbox.y1 > y &&
+ svp->segs[i].bbox.x0 < x1) {
+ seg = &svp->segs[i];
+ /* move cursor to topmost vector which overlaps [y,y+1) */
+ for (curs = 0; seg->points[curs + 1].y < y; curs++)
+ ;
+ cursor[i] = curs;
+ dy = seg->points[curs + 1].y - seg->points[curs].y;
+ if (fabs(dy) >= EPSILON_6)
+ seg_dx[i] = (seg->points[curs + 1].x - seg->points[curs].x) /
+ dy;
+ else
+ seg_dx[i] = 1e12;
+ seg_x[i] = seg->points[curs].x +
+ (y - seg->points[curs].y) * seg_dx[i];
+ art_svp_render_insert_active(i, active_segs, n_active_segs++,
+ seg_x, seg_dx);
+ }
+ }
+
+ n_steps = 0;
+
+ /* render the runlengths, advancing and deleting as we go */
+ start = 0x8000;
+
+ for (j = 0; j < n_active_segs; j++) {
+ seg_index = active_segs[j];
+ seg = &svp->segs[seg_index];
+ curs = cursor[seg_index];
+ while (curs != seg->n_points - 1 &&
+ seg->points[curs].y < y + 1) {
+ y_top = y;
+ if (y_top < seg->points[curs].y)
+ y_top = seg->points[curs].y;
+ y_bot = y + 1;
+ if (y_bot > seg->points[curs + 1].y)
+ y_bot = seg->points[curs + 1].y;
+ if (y_top != y_bot) {
+ delta = (seg->dir ? 16711680.0 : -16711680.0) *
+ (y_bot - y_top);
+ x_top = seg_x[seg_index] + (y_top - y) * seg_dx[seg_index];
+ x_bot = seg_x[seg_index] + (y_bot - y) * seg_dx[seg_index];
+ if (x_top < x_bot) {
+ x_min = x_top;
+ x_max = x_bot;
+ } else {
+ x_min = x_bot;
+ x_max = x_top;
+ }
+ ix_min = (int)floor(x_min);
+ ix_max = (int)floor(x_max);
+ if (ix_min >= x1) {
+ /* skip; it starts to the right of the render region */
+ } else if (ix_max < x0)
+ /* it ends to the left of the render region */
+ start += (int)delta;
+ else if (ix_min == ix_max) {
+ /* case 1, antialias a single pixel */
+ xdelta = (int)((ix_min + 1 - (x_min + x_max) * 0.5) * delta);
+
+ ADD_STEP(ix_min, xdelta)
+
+ if (ix_min + 1 < x1) {
+ xdelta = (int)(delta - xdelta);
+
+ ADD_STEP(ix_min + 1, xdelta)
+ }
+ } else {
+ /* case 2, antialias a run */
+ rslope = 1.0 / fabs(seg_dx[seg_index]);
+ drslope = delta * rslope;
+ last =
+ (int)(drslope * 0.5 *
+ (ix_min + 1 - x_min) * (ix_min + 1 - x_min));
+ xdelta = last;
+ if (ix_min >= x0) {
+ ADD_STEP(ix_min, xdelta)
+
+ x = ix_min + 1;
+ } else {
+ start += last;
+ x = x0;
+ }
+ if (ix_max > x1)
+ ix_max = x1;
+ for (; x < ix_max; x++) {
+ this_ = (int)((seg->dir ? 16711680.0 : -16711680.0) * rslope *
+ (x + 0.5 - x_min));
+ xdelta = this_ - last;
+ last = this_;
+
+ ADD_STEP(x, xdelta)
+ }
+ if (x < x1) {
+ this_ =
+ (int)(delta * (1 - 0.5 *
+ (x_max - ix_max) * (x_max - ix_max) *
+ rslope));
+ xdelta = this_ - last;
+ last = this_;
+
+ ADD_STEP(x, xdelta)
+
+ if (x + 1 < x1) {
+ xdelta = (int)(delta - last);
+
+ ADD_STEP(x + 1, xdelta)
+ }
+ }
+ }
+ }
+ curs++;
+ if (curs != seg->n_points - 1 &&
+ seg->points[curs].y < y + 1) {
+ dy = seg->points[curs + 1].y - seg->points[curs].y;
+ if (fabs(dy) >= EPSILON_6)
+ seg_dx[seg_index] = (seg->points[curs + 1].x -
+ seg->points[curs].x) / dy;
+ else
+ seg_dx[seg_index] = 1e12;
+ seg_x[seg_index] = seg->points[curs].x +
+ (y - seg->points[curs].y) * seg_dx[seg_index];
+ }
+ /* break here, instead of duplicating predicate in while? */
+ }
+ if (seg->points[curs].y >= y + 1) {
+ curs--;
+ cursor[seg_index] = curs;
+ seg_x[seg_index] += seg_dx[seg_index];
+ } else {
+ art_svp_render_delete_active(active_segs, j--,
+ --n_active_segs);
+ }
+ }
+
+ *p_start = start;
+ *p_steps = steps;
+ *p_n_steps = n_steps;
+
+ iter->seg_ix = i;
+ iter->n_active_segs = n_active_segs;
+ iter->y++;
+}
+
+void art_svp_render_aa_iter_done(ArtSVPRenderAAIter *iter) {
+ free(iter->steps);
+
+ free(iter->seg_dx);
+ free(iter->seg_x);
+ free(iter->cursor);
+ free(iter->active_segs);
+ free(iter);
+}
+
+/**
+ * art_svp_render_aa: Render SVP antialiased.
+ * @svp: The #ArtSVP to render.
+ * @x0: Left coordinate of destination rectangle.
+ * @y0: Top coordinate of destination rectangle.
+ * @x1: Right coordinate of destination rectangle.
+ * @y1: Bottom coordinate of destination rectangle.
+ * @callback: The callback which actually paints the pixels.
+ * @callback_data: Private data for @callback.
+ *
+ * Renders the sorted vector path in the given rectangle, antialiased.
+ *
+ * This interface uses a callback for the actual pixel rendering. The
+ * callback is called @y1 - @y0 times (once for each scan line). The y
+ * coordinate is given as an argument for convenience (it could be
+ * stored in the callback's private data and incremented on each
+ * call).
+ *
+ * The rendered polygon is represented in a semi-runlength format: a
+ * start value and a sequence of "steps". Each step has an x
+ * coordinate and a value delta. The resulting value at position x is
+ * equal to the sum of the start value and all step delta values for
+ * which the step x coordinate is less than or equal to x. An
+ * efficient algorithm will traverse the steps left to right, keeping
+ * a running sum.
+ *
+ * All x coordinates in the steps are guaranteed to be @x0 <= x < @x1.
+ * (This guarantee is a change from the gfonted vpaar renderer from
+ * which this routine is derived, and is designed to simplify the
+ * callback).
+ *
+ * The value 0x8000 represents 0% coverage by the polygon, while
+ * 0xff8000 represents 100% coverage. This format is designed so that
+ * >> 16 results in a standard 0x00..0xff value range, with nice
+ * rounding.
+ *
+ **/
+void art_svp_render_aa(const ArtSVP *svp,
+ int x0, int y0, int x1, int y1,
+ void (*callback)(void *callback_data,
+ int y,
+ int start,
+ ArtSVPRenderAAStep *steps, int n_steps),
+ void *callback_data) {
+ ArtSVPRenderAAIter *iter;
+ int y;
+ int start;
+ ArtSVPRenderAAStep *steps;
+ int n_steps;
+
+ iter = art_svp_render_aa_iter(svp, x0, y0, x1, y1);
+
+
+ for (y = y0; y < y1; y++) {
+ art_svp_render_aa_iter_step(iter, &start, &steps, &n_steps);
+ (*callback)(callback_data, y, start, steps, n_steps);
+ }
+
+ art_svp_render_aa_iter_done(iter);
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/gfx/image/art.h b/engines/sword25/gfx/image/art.h
new file mode 100644
index 0000000000..90baa770cf
--- /dev/null
+++ b/engines/sword25/gfx/image/art.h
@@ -0,0 +1,279 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * This code is based on Libart_LGPL - library of basic graphic primitives
+ *
+ * Copyright (c) 1998 Raph Levien
+ *
+ * Licensed under GNU LGPL v2
+ *
+ */
+
+/* Simple macros to set up storage allocation and basic types for libart
+ functions. */
+
+#ifndef __ART_H__
+#define __ART_H__
+
+#include "common/scummsys.h"
+
+namespace Sword25 {
+
+typedef byte art_u8;
+typedef uint16 art_u16;
+typedef uint32 art_u32;
+
+/* These aren't, strictly speaking, configuration macros, but they're
+ damn handy to have around, and may be worth playing with for
+ debugging. */
+#define art_new(type, n) ((type *)malloc ((n) * sizeof(type)))
+
+#define art_renew(p, type, n) ((type *)realloc (p, (n) * sizeof(type)))
+
+/* This one must be used carefully - in particular, p and max should
+ be variables. They can also be pstruct->el lvalues. */
+#define art_expand(p, type, max) do { if(max) { p = art_renew (p, type, max <<= 1); } else { max = 1; p = art_new(type, 1); } } while (0)
+
+typedef int art_boolean;
+#define ART_FALSE 0
+#define ART_TRUE 1
+
+/* define pi */
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif /* M_PI */
+
+#ifndef M_SQRT2
+#define M_SQRT2 1.41421356237309504880 /* sqrt(2) */
+#endif /* M_SQRT2 */
+
+/* Provide macros to feature the GCC function attribute.
+ */
+#if defined(__GNUC__) && (__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4))
+#define ART_GNUC_PRINTF( format_idx, arg_idx ) \
+ __attribute__((__format__ (__printf__, format_idx, arg_idx)))
+#define ART_GNUC_NORETURN \
+ __attribute__((__noreturn__))
+#else /* !__GNUC__ */
+#define ART_GNUC_PRINTF( format_idx, arg_idx )
+#define ART_GNUC_NORETURN
+#endif /* !__GNUC__ */
+
+void ART_GNUC_NORETURN
+art_die(const char *fmt, ...) ART_GNUC_PRINTF(1, 2);
+
+void
+art_warn(const char *fmt, ...) ART_GNUC_PRINTF(1, 2);
+
+typedef struct _ArtDRect ArtDRect;
+typedef struct _ArtIRect ArtIRect;
+
+struct _ArtDRect {
+ /*< public >*/
+ double x0, y0, x1, y1;
+};
+
+struct _ArtIRect {
+ /*< public >*/
+ int x0, y0, x1, y1;
+};
+
+typedef struct _ArtPoint ArtPoint;
+
+struct _ArtPoint {
+ /*< public >*/
+ double x, y;
+};
+
+/* Basic data structures and constructors for sorted vector paths */
+
+typedef struct _ArtSVP ArtSVP;
+typedef struct _ArtSVPSeg ArtSVPSeg;
+
+struct _ArtSVPSeg {
+ int n_points;
+ int dir; /* == 0 for "up", 1 for "down" */
+ ArtDRect bbox;
+ ArtPoint *points;
+};
+
+struct _ArtSVP {
+ int n_segs;
+ ArtSVPSeg segs[1];
+};
+
+void
+art_svp_free(ArtSVP *svp);
+
+int
+art_svp_seg_compare(const void *s1, const void *s2);
+
+/* Basic data structures and constructors for bezier paths */
+
+typedef enum {
+ ART_MOVETO,
+ ART_MOVETO_OPEN,
+ ART_CURVETO,
+ ART_LINETO,
+ ART_END
+} ArtPathcode;
+
+typedef struct _ArtBpath ArtBpath;
+
+struct _ArtBpath {
+ /*< public >*/
+ ArtPathcode code;
+ double x1;
+ double y1;
+ double x2;
+ double y2;
+ double x3;
+ double y3;
+};
+
+/* Basic data structures and constructors for simple vector paths */
+
+typedef struct _ArtVpath ArtVpath;
+
+/* CURVETO is not allowed! */
+struct _ArtVpath {
+ ArtPathcode code;
+ double x;
+ double y;
+};
+
+/* Some of the functions need to go into their own modules */
+
+void
+art_vpath_add_point(ArtVpath **p_vpath, int *pn_points, int *pn_points_max,
+ ArtPathcode code, double x, double y);
+
+ArtVpath *art_bez_path_to_vec(const ArtBpath *bez, double flatness);
+
+/* The funky new SVP intersector. */
+
+#ifndef ART_WIND_RULE_DEFINED
+#define ART_WIND_RULE_DEFINED
+typedef enum {
+ ART_WIND_RULE_NONZERO,
+ ART_WIND_RULE_INTERSECT,
+ ART_WIND_RULE_ODDEVEN,
+ ART_WIND_RULE_POSITIVE
+} ArtWindRule;
+#endif
+
+typedef struct _ArtSvpWriter ArtSvpWriter;
+
+struct _ArtSvpWriter {
+ int (*add_segment)(ArtSvpWriter *self, int wind_left, int delta_wind,
+ double x, double y);
+ void (*add_point)(ArtSvpWriter *self, int seg_id, double x, double y);
+ void (*close_segment)(ArtSvpWriter *self, int seg_id);
+};
+
+ArtSvpWriter *
+art_svp_writer_rewind_new(ArtWindRule rule);
+
+ArtSVP *
+art_svp_writer_rewind_reap(ArtSvpWriter *self);
+
+int
+art_svp_seg_compare(const void *s1, const void *s2);
+
+void
+art_svp_intersector(const ArtSVP *in, ArtSvpWriter *out);
+
+
+/* Sort vector paths into sorted vector paths. */
+
+ArtSVP *
+art_svp_from_vpath(ArtVpath *vpath);
+
+/* Sort vector paths into sorted vector paths. */
+
+typedef enum {
+ ART_PATH_STROKE_JOIN_MITER,
+ ART_PATH_STROKE_JOIN_ROUND,
+ ART_PATH_STROKE_JOIN_BEVEL
+} ArtPathStrokeJoinType;
+
+typedef enum {
+ ART_PATH_STROKE_CAP_BUTT,
+ ART_PATH_STROKE_CAP_ROUND,
+ ART_PATH_STROKE_CAP_SQUARE
+} ArtPathStrokeCapType;
+
+ArtSVP *
+art_svp_vpath_stroke(ArtVpath *vpath,
+ ArtPathStrokeJoinType join,
+ ArtPathStrokeCapType cap,
+ double line_width,
+ double miter_limit,
+ double flatness);
+
+/* This version may have winding numbers exceeding 1. */
+ArtVpath *
+art_svp_vpath_stroke_raw(ArtVpath *vpath,
+ ArtPathStrokeJoinType join,
+ ArtPathStrokeCapType cap,
+ double line_width,
+ double miter_limit,
+ double flatness);
+
+
+/* The spiffy antialiased renderer for sorted vector paths. */
+
+typedef struct _ArtSVPRenderAAStep ArtSVPRenderAAStep;
+typedef struct _ArtSVPRenderAAIter ArtSVPRenderAAIter;
+
+struct _ArtSVPRenderAAStep {
+ int x;
+ int delta; /* stored with 16 fractional bits */
+};
+
+ArtSVPRenderAAIter *
+art_svp_render_aa_iter(const ArtSVP *svp,
+ int x0, int y0, int x1, int y1);
+
+void
+art_svp_render_aa_iter_step(ArtSVPRenderAAIter *iter, int *p_start,
+ ArtSVPRenderAAStep **p_steps, int *p_n_steps);
+
+void
+art_svp_render_aa_iter_done(ArtSVPRenderAAIter *iter);
+
+void
+art_svp_render_aa(const ArtSVP *svp,
+ int x0, int y0, int x1, int y1,
+ void (*callback)(void *callback_data,
+ int y,
+ int start,
+ ArtSVPRenderAAStep *steps, int n_steps),
+ void *callback_data);
+
+} // End of namespace Sword25
+
+#endif /* __ART_H__ */
diff --git a/engines/sword25/gfx/image/image.h b/engines/sword25/gfx/image/image.h
new file mode 100644
index 0000000000..5ac6d1ac25
--- /dev/null
+++ b/engines/sword25/gfx/image/image.h
@@ -0,0 +1,222 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+/*
+ BS_Image
+ --------
+
+ Autor: Malte Thiesen
+*/
+
+#ifndef SWORD25_IMAGE_H
+#define SWORD25_IMAGE_H
+
+// Includes
+#include "sword25/kernel/common.h"
+#include "common/rect.h"
+#include "sword25/gfx/graphicengine.h"
+
+namespace Sword25 {
+
+class Image {
+public:
+ virtual ~Image() {}
+
+ // Enums
+ /**
+ @brief Die möglichen Flippingparameter für die Blit-Methode.
+ */
+ enum FLIP_FLAGS {
+ /// Das Bild wird nicht gespiegelt.
+ FLIP_NONE = 0,
+ /// Das Bild wird an der horizontalen Achse gespiegelt.
+ FLIP_H = 1,
+ /// Das Bild wird an der vertikalen Achse gespiegelt.
+ FLIP_V = 2,
+ /// Das Bild wird an der horizontalen und vertikalen Achse gespiegelt.
+ FLIP_HV = FLIP_H | FLIP_V,
+ /// Das Bild wird an der horizontalen und vertikalen Achse gespiegelt.
+ FLIP_VH = FLIP_H | FLIP_V
+ };
+
+ //@{
+ /** @name Accessor-Methoden */
+
+ /**
+ @brief Gibt die Breite des Bildes in Pixeln zurück
+ */
+ virtual int getWidth() const = 0;
+
+ /**
+ @brief Gibt die Höhe des Bildes in Pixeln zurück
+ */
+ virtual int getHeight() const = 0;
+
+ /**
+ @brief Gibt das Farbformat des Bildes zurück
+ */
+ virtual GraphicEngine::COLOR_FORMATS getColorFormat() const = 0;
+
+ //@}
+
+ //@{
+ /** @name Render-Methoden */
+
+ /**
+ @brief Rendert das Bild in den Framebuffer.
+ @param pDest ein Pointer auf das Zielbild. In den meisten Fällen ist dies der Framebuffer.
+ @param PosX die Position auf der X-Achse im Zielbild in Pixeln, an der das Bild gerendert werden soll.<br>
+ Der Standardwert ist 0.
+ @param PosY die Position auf der Y-Achse im Zielbild in Pixeln, an der das Bild gerendert werden soll.<br>
+ Der Standardwert ist 0.
+ @param Flipping gibt an, wie das Bild gespiegelt werden soll.<br>
+ Der Standardwert ist BS_Image::FLIP_NONE (keine Spiegelung)
+ @param pSrcPartRect Pointer auf ein Common::Rect, welches den Ausschnitt des Quellbildes spezifiziert, der gerendert
+ werden soll oder NULL, falls das gesamte Bild gerendert werden soll.<br>
+ Dieser Ausschnitt bezieht sich auf das ungespiegelte und unskalierte Bild.<br>
+ Der Standardwert ist NULL.
+ @param Color ein ARGB Farbwert, der die Parameter für die Farbmodulation und fürs Alphablending festlegt.<br>
+ Die Alpha-Komponente der Farbe bestimmt den Alphablending Parameter (0 = keine Deckung, 255 = volle Deckung).<br>
+ Die Farbkomponenten geben die Farbe für die Farbmodulation an.<br>
+ Der Standardwert is BS_ARGB(255, 255, 255, 255) (volle Deckung, keine Farbmodulation).
+ Zum Erzeugen des Farbwertes können die Makros BS_RGB und BS_ARGB benutzt werden.
+ @param Width gibt die Ausgabebreite des Bildausschnittes an.
+ Falls diese von der Breite des Bildausschnittes abweicht wird
+ das Bild entsprechend Skaliert.<br>
+ Der Wert -1 gibt an, dass das Bild nicht Skaliert werden soll.<br>
+ Der Standardwert ist -1.
+ @param Width gibt die Ausgabehöhe des Bildausschnittes an.
+ Falls diese von der Höhe des Bildauschnittes abweicht, wird
+ das Bild entsprechend Skaliert.<br>
+ Der Wert -1 gibt an, dass das Bild nicht Skaliert werden soll.<br>
+ Der Standardwert ist -1.
+ @return Gibt false zurück, falls das Rendern fehlgeschlagen ist.
+ @remark Er werden nicht alle Blitting-Operationen von allen BS_Image-Klassen unterstützt.<br>
+ Mehr Informationen gibt es in der Klassenbeschreibung von BS_Image und durch folgende Methoden:
+ - IsBlitTarget()
+ - IsScalingAllowed()
+ - IsFillingAllowed()
+ - IsAlphaAllowed()
+ - IsColorModulationAllowed()
+ - IsSetContentAllowed()
+ */
+ virtual bool blit(int posX = 0, int posY = 0,
+ int flipping = FLIP_NONE,
+ Common::Rect *pPartRect = NULL,
+ uint color = BS_ARGB(255, 255, 255, 255),
+ int width = -1, int height = -1) = 0;
+
+ /**
+ @brief Füllt einen Rechteckigen Bereich des Bildes mit einer Farbe.
+ @param pFillRect Pointer auf ein Common::Rect, welches den Ausschnitt des Bildes spezifiziert, der gefüllt
+ werden soll oder NULL, falls das gesamte Bild gefüllt werden soll.<br>
+ Der Standardwert ist NULL.
+ @param Color der 32 Bit Farbwert mit dem der Bildbereich gefüllt werden soll.
+ @remark Es ist möglich über die Methode transparente Rechtecke darzustellen, indem man eine Farbe mit einem Alphawert ungleich
+ 255 angibt.
+ @remark Unabhängig vom Farbformat des Bildes muss ein 32 Bit Farbwert angegeben werden. Zur Erzeugung, können die Makros
+ BS_RGB und BS_ARGB benutzt werden.
+ @remark Falls das Rechteck nicht völlig innerhalb des Bildschirms ist, wird es automatisch zurechtgestutzt.
+ */
+ virtual bool fill(const Common::Rect *pFillRect = 0, uint color = BS_RGB(0, 0, 0)) = 0;
+
+ /**
+ @brief Füllt den Inhalt des Bildes mit Pixeldaten.
+ @param Pixeldata ein Vector der die Pixeldaten enthält. Sie müssen in dem Farbformat des Bildes vorliegen und es müssen genügend Daten
+ vorhanden sein, um das ganze Bild zu füllen.
+ @param Offset der Offset in Byte im Pixeldata-Vector an dem sich der erste zu schreibende Pixel befindet.<br>
+ Der Standardwert ist 0.
+ @param Stride der Abstand in Byte zwischen dem Zeilenende und dem Beginn einer neuen Zeile im Pixeldata-Vector.<br>
+ Der Standardwert ist 0.
+ @return Gibt false zurück, falls der Aufruf fehlgeschlagen ist.
+ @remark Ein Aufruf dieser Methode ist nur erlaubt, wenn IsSetContentAllowed() true zurückgibt.
+ */
+ virtual bool setContent(const byte *pixeldata, uint size, uint offset, uint stride) = 0;
+
+ /**
+ @brief Liest einen Pixel des Bildes.
+ @param X die X-Koordinate des Pixels.
+ @param Y die Y-Koordinate des Pixels
+ @return Gibt den 32-Bit Farbwert des Pixels an der übergebenen Koordinate zurück.
+ @remark Diese Methode sollte auf keine Fall benutzt werden um größere Teile des Bildes zu lesen, da sie sehr langsam ist. Sie ist
+ eher dafür gedacht einzelne Pixel des Bildes auszulesen.
+ */
+ virtual uint getPixel(int x, int y) = 0;
+
+ //@{
+ /** @name Auskunfts-Methoden */
+
+ /**
+ @brief Überprüft, ob an dem BS_Image Blit() aufgerufen werden darf.
+ @return Gibt false zurück, falls ein Blit()-Aufruf an diesem Objekt nicht gestattet ist.
+ */
+ virtual bool isBlitSource() const = 0;
+
+ /**
+ @brief Überprüft, ob das BS_Image ein Zielbild für einen Blit-Aufruf sein kann.
+ @return Gibt false zurück, falls ein Blit-Aufruf mit diesem Objekt als Ziel nicht gestattet ist.
+ */
+ virtual bool isBlitTarget() const = 0;
+
+ /**
+ @brief Gibt true zurück, falls das BS_Image bei einem Aufruf von Blit() skaliert dargestellt werden kann.
+ */
+ virtual bool isScalingAllowed() const = 0;
+
+ /**
+ @brief Gibt true zurück, wenn das BS_Image mit einem Aufruf von Fill() gefüllt werden kann.
+ */
+ virtual bool isFillingAllowed() const = 0;
+
+ /**
+ @brief Gibt true zurück, wenn das BS_Image bei einem Aufruf von Blit() mit einem Alphawert dargestellt werden kann.
+ */
+ virtual bool isAlphaAllowed() const = 0;
+
+ /**
+ @brief Gibt true zurück, wenn das BS_Image bei einem Aufruf von Blit() mit Farbmodulation dargestellt werden kann.
+ */
+ virtual bool isColorModulationAllowed() const = 0;
+
+ /**
+ @brief Gibt true zurück, wenn der Inhalt des BS_Image durch eine Aufruf von SetContent() ausgetauscht werden kann.
+ */
+ virtual bool isSetContentAllowed() const = 0;
+
+ //@}
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/gfx/image/pngloader.cpp b/engines/sword25/gfx/image/pngloader.cpp
new file mode 100644
index 0000000000..1b72595a8f
--- /dev/null
+++ b/engines/sword25/gfx/image/pngloader.cpp
@@ -0,0 +1,255 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+// Disable symbol overrides so that we can use png.h
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
+#include "sword25/gfx/image/image.h"
+#include "sword25/gfx/image/pngloader.h"
+#include <png.h>
+
+namespace Sword25 {
+
+#define BS_LOG_PREFIX "PNGLOADER"
+
+
+/**
+ * Load a NULL-terminated string from the given stream.
+ */
+static Common::String loadString(Common::ReadStream &in, uint maxSize = 999) {
+ Common::String result;
+
+ while (!in.eos() && (result.size() < maxSize)) {
+ char ch = (char)in.readByte();
+ if (ch == '\0')
+ break;
+
+ result += ch;
+ }
+
+ return result;
+}
+
+/**
+ * Check if the given data is a savegame, and if so, locate the
+ * offset to the image data.
+ * @return offset to image data if fileDataPtr contains a savegame; 0 otherwise
+ */
+static uint findEmbeddedPNG(const byte *fileDataPtr, uint fileSize) {
+ if (fileSize < 100)
+ return 0;
+ if (memcmp(fileDataPtr, "BS25SAVEGAME", 12))
+ return 0;
+
+ // Read in the header
+ Common::MemoryReadStream stream(fileDataPtr, fileSize);
+ stream.seek(0, SEEK_SET);
+
+ // Headerinformationen der Spielstandes einlesen.
+ uint compressedGamedataSize;
+ loadString(stream); // Marker
+ loadString(stream); // Version
+ loadString(stream); // Description
+ Common::String gameSize = loadString(stream);
+ compressedGamedataSize = atoi(gameSize.c_str());
+ loadString(stream);
+
+ // Return the offset of where the thumbnail starts
+ return static_cast<uint>(stream.pos() + compressedGamedataSize);
+}
+
+static void png_user_read_data(png_structp png_ptr, png_bytep data, png_size_t length) {
+ const byte **ref = (const byte **)png_get_io_ptr(png_ptr);
+ memcpy(data, *ref, length);
+ *ref += length;
+}
+
+static bool doIsCorrectImageFormat(const byte *fileDataPtr, uint fileSize) {
+ return (fileSize > 8) && png_check_sig(const_cast<byte *>(fileDataPtr), 8);
+}
+
+
+bool PNGLoader::doDecodeImage(const byte *fileDataPtr, uint fileSize, byte *&uncompressedDataPtr, int &width, int &height, int &pitch) {
+ png_structp png_ptr = NULL;
+ png_infop info_ptr = NULL;
+
+ int bitDepth;
+ int colorType;
+ int interlaceType;
+ int i;
+
+ // Check for valid PNG signature
+ if (!doIsCorrectImageFormat(fileDataPtr, fileSize)) {
+ error("png_check_sig failed");
+ }
+
+ // Die beiden PNG Strukturen erstellen
+ png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if (!png_ptr) {
+ error("Could not create libpng read struct.");
+ }
+
+ info_ptr = png_create_info_struct(png_ptr);
+ if (!info_ptr) {
+ error("Could not create libpng info struct.");
+ }
+
+ // Alternative Lesefunktion benutzen
+ const byte **ref = &fileDataPtr;
+ png_set_read_fn(png_ptr, (void *)ref, png_user_read_data);
+
+ // PNG Header einlesen
+ png_read_info(png_ptr, info_ptr);
+
+ // PNG Informationen auslesen
+ png_get_IHDR(png_ptr, info_ptr, (png_uint_32 *)&width, (png_uint_32 *)&height, &bitDepth, &colorType, &interlaceType, NULL, NULL);
+
+ // Pitch des Ausgabebildes berechnen
+ pitch = GraphicEngine::calcPitch(GraphicEngine::CF_ARGB32, width);
+
+ // Speicher für die endgültigen Bilddaten reservieren
+ // Dieses geschieht vor dem reservieren von Speicher für temporäre Bilddaten um die Fragmentierung des Speichers gering zu halten
+ uncompressedDataPtr = new byte[pitch * height];
+ if (!uncompressedDataPtr) {
+ error("Could not allocate memory for output image.");
+ }
+
+ // Bilder jeglicher Farbformate werden zunächst in ARGB Bilder umgewandelt
+ if (bitDepth == 16)
+ png_set_strip_16(png_ptr);
+ if (colorType == PNG_COLOR_TYPE_PALETTE)
+ png_set_expand(png_ptr);
+ if (bitDepth < 8)
+ png_set_expand(png_ptr);
+ if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
+ png_set_expand(png_ptr);
+ if (colorType == PNG_COLOR_TYPE_GRAY ||
+ colorType == PNG_COLOR_TYPE_GRAY_ALPHA)
+ png_set_gray_to_rgb(png_ptr);
+
+ png_set_bgr(png_ptr);
+
+ if (colorType != PNG_COLOR_TYPE_RGB_ALPHA)
+ png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
+
+ // Nachdem die Transformationen registriert wurden, werden die Bilddaten erneut eingelesen
+ png_read_update_info(png_ptr, info_ptr);
+ png_get_IHDR(png_ptr, info_ptr, (png_uint_32 *)&width, (png_uint_32 *)&height, &bitDepth, &colorType, NULL, NULL, NULL);
+
+ if (interlaceType == PNG_INTERLACE_NONE) {
+ // PNGs without interlacing can simply be read row by row.
+ for (i = 0; i < height; i++) {
+ png_read_row(png_ptr, uncompressedDataPtr + i * pitch, NULL);
+ }
+ } else {
+ // PNGs with interlacing require us to allocate an auxillary
+ // buffer with pointers to all row starts.
+
+ // Allocate row pointer buffer
+ png_bytep *pRowPtr = new png_bytep[height];
+ if (!pRowPtr) {
+ error("Could not allocate memory for row pointers.");
+ }
+
+ // Initialize row pointers
+ for (i = 0; i < height; i++)
+ pRowPtr[i] = uncompressedDataPtr + i * pitch;
+
+ // Read image data
+ png_read_image(png_ptr, pRowPtr);
+
+ // Free row pointer buffer
+ delete[] pRowPtr;
+ }
+
+ // Read additional data at the end.
+ png_read_end(png_ptr, NULL);
+
+ // Destroy libpng structures
+ png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+
+ // Signal success
+ return true;
+}
+
+bool PNGLoader::decodeImage(const byte *fileDataPtr, uint fileSize, byte *&uncompressedDataPtr, int &width, int &height, int &pitch) {
+ uint pngOffset = findEmbeddedPNG(fileDataPtr, fileSize);
+ return doDecodeImage(fileDataPtr + pngOffset, fileSize - pngOffset, uncompressedDataPtr, width, height, pitch);
+}
+
+bool PNGLoader::doImageProperties(const byte *fileDataPtr, uint fileSize, int &width, int &height) {
+ // Check for valid PNG signature
+ if (!doIsCorrectImageFormat(fileDataPtr, fileSize))
+ return false;
+
+ png_structp png_ptr = NULL;
+ png_infop info_ptr = NULL;
+
+ // Die beiden PNG Strukturen erstellen
+ png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if (!png_ptr) {
+ error("Could not create libpng read struct.");
+ }
+
+ info_ptr = png_create_info_struct(png_ptr);
+ if (!info_ptr) {
+ error("Could not create libpng info struct.");
+ }
+
+ // Alternative Lesefunktion benutzen
+ const byte **ref = &fileDataPtr;
+ png_set_read_fn(png_ptr, (void *)ref, png_user_read_data);
+
+ // PNG Header einlesen
+ png_read_info(png_ptr, info_ptr);
+
+ // PNG Informationen auslesen
+ int bitDepth;
+ int colorType;
+ png_get_IHDR(png_ptr, info_ptr, (png_uint_32 *)&width, (png_uint_32 *)&height, &bitDepth, &colorType, NULL, NULL, NULL);
+
+ // Die Strukturen freigeben
+ png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+
+ return true;
+
+}
+
+bool PNGLoader::imageProperties(const byte *fileDataPtr, uint fileSize, int &width, int &height) {
+ uint pngOffset = findEmbeddedPNG(fileDataPtr, fileSize);
+ return doImageProperties(fileDataPtr + pngOffset, fileSize - pngOffset, width, height);
+}
+
+
+} // End of namespace Sword25
diff --git a/engines/sword25/gfx/image/pngloader.h b/engines/sword25/gfx/image/pngloader.h
new file mode 100644
index 0000000000..e0d68ff8b9
--- /dev/null
+++ b/engines/sword25/gfx/image/pngloader.h
@@ -0,0 +1,93 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#ifndef SWORD25_PNGLOADER2_H
+#define SWORD25_PNGLOADER2_H
+
+#include "sword25/kernel/common.h"
+#include "sword25/gfx/graphicengine.h"
+
+namespace Sword25 {
+
+/**
+ * Class for loading PNG files, and PNG data embedded into savegames.
+ *
+ * Originally written by Malte Thiesen.
+ */
+class PNGLoader {
+protected:
+ PNGLoader() {} // Protected constructor to prevent instances
+
+ static bool doDecodeImage(const byte *fileDataPtr, uint fileSize, byte *&uncompressedDataPtr, int &width, int &height, int &pitch);
+ static bool doImageProperties(const byte *fileDataPtr, uint fileSize, int &width, int &height);
+
+public:
+
+ /**
+ * Decode an image.
+ * @param[in] fileDatePtr pointer to the image data
+ * @param[in] fileSize size of the image data in bytes
+ * @param[out] pUncompressedData if successful, this is set to a pointer containing the decoded image data
+ * @param[out] width if successful, this is set to the width of the image
+ * @param[out] height if successful, this is set to the height of the image
+ * @param[out] pitch if successful, this is set to the number of bytes per scanline in the image
+ * @return false in case of an error
+ *
+ * @remark The size of the output data equals pitch * height.
+ * @remark This function does not free the image buffer passed to it,
+ * it is the callers responsibility to do so.
+ */
+ static bool decodeImage(const byte *pFileData, uint fileSize,
+ byte *&pUncompressedData,
+ int &width, int &height,
+ int &pitch);
+ /**
+ * Extract the properties of an image.
+ * @param[in] fileDatePtr pointer to the image data
+ * @param[in] fileSize size of the image data in bytes
+ * @param[out] width if successful, this is set to the width of the image
+ * @param[out] height if successful, this is set to the height of the image
+ * @return returns true if extraction of the properties was successful, false in case of an error
+ *
+ * @remark This function does not free the image buffer passed to it,
+ * it is the callers responsibility to do so.
+ */
+ static bool imageProperties(const byte *fileDatePtr, uint fileSize,
+ int &width,
+ int &height);
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/gfx/image/renderedimage.cpp b/engines/sword25/gfx/image/renderedimage.cpp
new file mode 100644
index 0000000000..9392eca044
--- /dev/null
+++ b/engines/sword25/gfx/image/renderedimage.cpp
@@ -0,0 +1,397 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+// -----------------------------------------------------------------------------
+// INCLUDES
+// -----------------------------------------------------------------------------
+
+#include "sword25/package/packagemanager.h"
+#include "sword25/gfx/image/pngloader.h"
+#include "sword25/gfx/image/renderedimage.h"
+
+#include "common/system.h"
+
+namespace Sword25 {
+
+#define BS_LOG_PREFIX "RENDEREDIMAGE"
+
+// -----------------------------------------------------------------------------
+// CONSTRUCTION / DESTRUCTION
+// -----------------------------------------------------------------------------
+
+RenderedImage::RenderedImage(const Common::String &filename, bool &result) :
+ _data(0),
+ _width(0),
+ _height(0) {
+ result = false;
+
+ PackageManager *pPackage = Kernel::getInstance()->getPackage();
+ BS_ASSERT(pPackage);
+
+ _backSurface = Kernel::getInstance()->getGfx()->getSurface();
+
+ // Datei laden
+ byte *pFileData;
+ uint fileSize;
+ if (!(pFileData = (byte *)pPackage->getFile(filename, &fileSize))) {
+ BS_LOG_ERRORLN("File \"%s\" could not be loaded.", filename.c_str());
+ return;
+ }
+
+ // Bildeigenschaften bestimmen
+ int pitch;
+ if (!PNGLoader::imageProperties(pFileData, fileSize, _width, _height)) {
+ BS_LOG_ERRORLN("Could not read image properties.");
+ delete[] pFileData;
+ return;
+ }
+
+ // Das Bild dekomprimieren
+ if (!PNGLoader::decodeImage(pFileData, fileSize, _data, _width, _height, pitch)) {
+ BS_LOG_ERRORLN("Could not decode image.");
+ delete[] pFileData;
+ return;
+ }
+
+ // Dateidaten freigeben
+ delete[] pFileData;
+
+ _doCleanup = true;
+
+ result = true;
+ return;
+}
+
+// -----------------------------------------------------------------------------
+
+RenderedImage::RenderedImage(uint width, uint height, bool &result) :
+ _width(width),
+ _height(height) {
+
+ _data = new byte[width * height * 4];
+ Common::set_to(_data, &_data[width * height * 4], 0);
+
+ _backSurface = Kernel::getInstance()->getGfx()->getSurface();
+
+ _doCleanup = true;
+
+ result = true;
+ return;
+}
+
+RenderedImage::RenderedImage() : _width(0), _height(0), _data(0) {
+ _backSurface = Kernel::getInstance()->getGfx()->getSurface();
+
+ _doCleanup = false;
+
+ return;
+}
+
+// -----------------------------------------------------------------------------
+
+RenderedImage::~RenderedImage() {
+ if (_doCleanup)
+ delete[] _data;
+}
+
+// -----------------------------------------------------------------------------
+
+bool RenderedImage::fill(const Common::Rect *pFillRect, uint color) {
+ BS_LOG_ERRORLN("Fill() is not supported.");
+ return false;
+}
+
+// -----------------------------------------------------------------------------
+
+bool RenderedImage::setContent(const byte *pixeldata, uint size, uint offset, uint stride) {
+ // Überprüfen, ob PixelData ausreichend viele Pixel enthält um ein Bild der Größe Width * Height zu erzeugen
+ if (size < static_cast<uint>(_width * _height * 4)) {
+ BS_LOG_ERRORLN("PixelData vector is too small to define a 32 bit %dx%d image.", _width, _height);
+ return false;
+ }
+
+ const byte *in = &pixeldata[offset];
+ byte *out = _data;
+
+ for (int i = 0; i < _height; i++) {
+ memcpy(out, in, _width * 4);
+ out += _width * 4;
+ in += stride;
+ }
+
+ return true;
+}
+
+void RenderedImage::replaceContent(byte *pixeldata, int width, int height) {
+ _width = width;
+ _height = height;
+ _data = pixeldata;
+}
+// -----------------------------------------------------------------------------
+
+uint RenderedImage::getPixel(int x, int y) {
+ BS_LOG_ERRORLN("GetPixel() is not supported. Returning black.");
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+bool RenderedImage::blit(int posX, int posY, int flipping, Common::Rect *pPartRect, uint color, int width, int height) {
+ int ca = (color >> 24) & 0xff;
+
+ // Check if we need to draw anything at all
+ if (ca == 0)
+ return true;
+
+ int cr = (color >> 16) & 0xff;
+ int cg = (color >> 8) & 0xff;
+ int cb = (color >> 0) & 0xff;
+
+ // Compensate for transparency. Since we're coming
+ // down to 255 alpha, we just compensate for the colors here
+ if (ca != 255) {
+ cr = cr * ca >> 8;
+ cg = cg * ca >> 8;
+ cb = cb * ca >> 8;
+ }
+
+ // Create an encapsulating surface for the data
+ Graphics::Surface srcImage;
+ srcImage.bytesPerPixel = 4;
+ srcImage.pitch = _width * 4;
+ srcImage.w = _width;
+ srcImage.h = _height;
+ srcImage.pixels = _data;
+
+ if (pPartRect) {
+ srcImage.pixels = &_data[pPartRect->top * srcImage.pitch + pPartRect->left * 4];
+ srcImage.w = pPartRect->right - pPartRect->left;
+ srcImage.h = pPartRect->bottom - pPartRect->top;
+
+ debug(6, "Blit(%d, %d, %d, [%d, %d, %d, %d], %08x, %d, %d)", posX, posY, flipping,
+ pPartRect->left, pPartRect->top, pPartRect->width(), pPartRect->height(), color, width, height);
+ } else {
+
+ debug(6, "Blit(%d, %d, %d, [%d, %d, %d, %d], %08x, %d, %d)", posX, posY, flipping, 0, 0,
+ srcImage.w, srcImage.h, color, width, height);
+ }
+
+ if (width == -1)
+ width = srcImage.w;
+ if (height == -1)
+ height = srcImage.h;
+
+#ifdef SCALING_TESTING
+ // Hardcode scaling to 66% to test scaling
+ width = width * 2 / 3;
+ height = height * 2 / 3;
+#endif
+
+ Graphics::Surface *img;
+ Graphics::Surface *imgScaled = NULL;
+ byte *savedPixels = NULL;
+ if ((width != srcImage.w) || (height != srcImage.h)) {
+ // Scale the image
+ img = imgScaled = scale(srcImage, width, height);
+ savedPixels = (byte *)img->pixels;
+ } else {
+ img = &srcImage;
+ }
+
+ // Handle off-screen clipping
+ if (posY < 0) {
+ img->h = MAX(0, (int)img->h - -posY);
+ img->pixels = (byte *)img->pixels + img->pitch * -posY;
+ posY = 0;
+ }
+
+ if (posX < 0) {
+ img->w = MAX(0, (int)img->w - -posX);
+ img->pixels = (byte *)img->pixels + (-posX * 4);
+ posX = 0;
+ }
+
+ img->w = CLIP((int)img->w, 0, (int)MAX((int)_backSurface->w - posX, 0));
+ img->h = CLIP((int)img->h, 0, (int)MAX((int)_backSurface->h - posY, 0));
+
+ if ((img->w > 0) && (img->h > 0)) {
+ int xp = 0, yp = 0;
+
+ int inStep = 4;
+ int inoStep = img->pitch;
+ if (flipping & Image::FLIP_V) {
+ inStep = -inStep;
+ xp = img->w - 1;
+ }
+
+ if (flipping & Image::FLIP_H) {
+ inoStep = -inoStep;
+ yp = img->h - 1;
+ }
+
+ byte *ino = (byte *)img->getBasePtr(xp, yp);
+ byte *outo = (byte *)_backSurface->getBasePtr(posX, posY);
+ byte *in, *out;
+
+ for (int i = 0; i < img->h; i++) {
+ out = outo;
+ in = ino;
+ for (int j = 0; j < img->w; j++) {
+ int r = in[0];
+ int g = in[1];
+ int b = in[2];
+ int a = in[3];
+ in += inStep;
+
+ if (ca != 255) {
+ a = a * ca >> 8;
+ }
+
+ switch (a) {
+ case 0: // Full transparency
+ out += 4;
+ break;
+ case 255: // Full opacity
+ if (cr != 255)
+ *out++ = (r * cr) >> 8;
+ else
+ *out++ = r;
+
+ if (cg != 255)
+ *out++ = (g * cg) >> 8;
+ else
+ *out++ = g;
+
+ if (cb != 255)
+ *out++ = (b * cb) >> 8;
+ else
+ *out++ = b;
+
+ *out++ = a;
+ break;
+
+ default: // alpha blending
+ if (cr != 255)
+ *out += ((r - *out) * a * cr) >> 16;
+ else
+ *out += ((r - *out) * a) >> 8;
+ out++;
+ if (cg != 255)
+ *out += ((g - *out) * a * cg) >> 16;
+ else
+ *out += ((g - *out) * a) >> 8;
+ out++;
+ if (cb != 255)
+ *out += ((b - *out) * a * cb) >> 16;
+ else
+ *out += ((b - *out) * a) >> 8;
+ out++;
+ *out = 255;
+ out++;
+ }
+ }
+ outo += _backSurface->pitch;
+ ino += inoStep;
+ }
+
+ g_system->copyRectToScreen((byte *)_backSurface->getBasePtr(posX, posY), _backSurface->pitch, posX, posY,
+ img->w, img->h);
+ }
+
+ if (imgScaled) {
+ imgScaled->pixels = savedPixels;
+ imgScaled->free();
+ delete imgScaled;
+ }
+
+ return true;
+}
+
+/**
+ * Scales a passed surface, creating a new surface with the result
+ * @param srcImage Source image to scale
+ * @param scaleFactor Scale amount. Must be between 0 and 1.0 (but not zero)
+ * @remarks Caller is responsible for freeing the returned surface
+ */
+Graphics::Surface *RenderedImage::scale(const Graphics::Surface &srcImage, int xSize, int ySize) {
+ Graphics::Surface *s = new Graphics::Surface();
+ s->create(xSize, ySize, srcImage.bytesPerPixel);
+
+ int *horizUsage = scaleLine(xSize, srcImage.w);
+ int *vertUsage = scaleLine(ySize, srcImage.h);
+
+ // Loop to create scaled version
+ for (int yp = 0; yp < ySize; ++yp) {
+ const byte *srcP = (const byte *)srcImage.getBasePtr(0, vertUsage[yp]);
+ byte *destP = (byte *)s->getBasePtr(0, yp);
+
+ for (int xp = 0; xp < xSize; ++xp) {
+ const byte *tempSrcP = srcP + (horizUsage[xp] * srcImage.bytesPerPixel);
+ for (int byteCtr = 0; byteCtr < srcImage.bytesPerPixel; ++byteCtr) {
+ *destP++ = *tempSrcP++;
+ }
+ }
+ }
+
+ // Delete arrays and return surface
+ delete[] horizUsage;
+ delete[] vertUsage;
+ return s;
+}
+
+/**
+ * Returns an array indicating which pixels of a source image horizontally or vertically get
+ * included in a scaled image
+ */
+int *RenderedImage::scaleLine(int size, int srcSize) {
+ int scale = 100 * size / srcSize;
+ assert(scale > 0);
+ int *v = new int[size];
+ Common::set_to(v, &v[size], 0);
+
+ int distCtr = 0;
+ int *destP = v;
+ for (int distIndex = 0; distIndex < srcSize; ++distIndex) {
+ distCtr += scale;
+ while (distCtr >= 100) {
+ assert(destP < &v[size]);
+ *destP++ = distIndex;
+ distCtr -= 100;
+ }
+ }
+
+ return v;
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/gfx/image/renderedimage.h b/engines/sword25/gfx/image/renderedimage.h
new file mode 100644
index 0000000000..a9f2f1823c
--- /dev/null
+++ b/engines/sword25/gfx/image/renderedimage.h
@@ -0,0 +1,121 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#ifndef SWORD25_RENDERED_IMAGE_H
+#define SWORD25_RENDERED_IMAGE_H
+
+// -----------------------------------------------------------------------------
+// INCLUDES
+// -----------------------------------------------------------------------------
+
+#include "sword25/kernel/common.h"
+#include "sword25/gfx/image/image.h"
+#include "sword25/gfx/graphicengine.h"
+
+namespace Sword25 {
+
+class RenderedImage : public Image {
+public:
+ RenderedImage(const Common::String &filename, bool &result);
+
+ /**
+ @brief Erzeugt ein leeres BS_RenderedImage
+
+ @param Width die Breite des zu erzeugenden Bildes.
+ @param Height die Höhe des zu erzeugenden Bildes
+ @param Result gibt dem Aufrufer bekannt, ob der Konstruktor erfolgreich ausgeführt wurde. Wenn es nach dem Aufruf false enthalten sollte,
+ dürfen keine Methoden am Objekt aufgerufen werden und das Objekt ist sofort zu zerstören.
+ */
+ RenderedImage(uint width, uint height, bool &result);
+ RenderedImage();
+
+ virtual ~RenderedImage();
+
+ virtual int getWidth() const {
+ return _width;
+ }
+ virtual int getHeight() const {
+ return _height;
+ }
+ virtual GraphicEngine::COLOR_FORMATS getColorFormat() const {
+ return GraphicEngine::CF_ARGB32;
+ }
+
+ virtual bool blit(int posX = 0, int posY = 0,
+ int flipping = Image::FLIP_NONE,
+ Common::Rect *pPartRect = NULL,
+ uint color = BS_ARGB(255, 255, 255, 255),
+ int width = -1, int height = -1);
+ virtual bool fill(const Common::Rect *pFillRect, uint color);
+ virtual bool setContent(const byte *pixeldata, uint size, uint offset = 0, uint stride = 0);
+ void replaceContent(byte *pixeldata, int width, int height);
+ virtual uint getPixel(int x, int y);
+
+ virtual bool isBlitSource() const {
+ return true;
+ }
+ virtual bool isBlitTarget() const {
+ return false;
+ }
+ virtual bool isScalingAllowed() const {
+ return true;
+ }
+ virtual bool isFillingAllowed() const {
+ return false;
+ }
+ virtual bool isAlphaAllowed() const {
+ return true;
+ }
+ virtual bool isColorModulationAllowed() const {
+ return true;
+ }
+ virtual bool isSetContentAllowed() const {
+ return true;
+ }
+
+ static Graphics::Surface *scale(const Graphics::Surface &srcImage, int xSize, int ySize);
+private:
+ byte *_data;
+ int _width;
+ int _height;
+ bool _doCleanup;
+
+ Graphics::Surface *_backSurface;
+
+ static int *scaleLine(int size, int srcSize);
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/gfx/image/swimage.cpp b/engines/sword25/gfx/image/swimage.cpp
new file mode 100644
index 0000000000..f0a8899bb6
--- /dev/null
+++ b/engines/sword25/gfx/image/swimage.cpp
@@ -0,0 +1,115 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#include "sword25/package/packagemanager.h"
+#include "sword25/gfx/image/pngloader.h"
+#include "sword25/gfx/image/swimage.h"
+
+namespace Sword25 {
+
+#define BS_LOG_PREFIX "SWIMAGE"
+
+
+SWImage::SWImage(const Common::String &filename, bool &result) :
+ _imageDataPtr(0),
+ _width(0),
+ _height(0) {
+ result = false;
+
+ PackageManager *pPackage = Kernel::getInstance()->getPackage();
+ BS_ASSERT(pPackage);
+
+ // Datei laden
+ byte *pFileData;
+ uint fileSize;
+ if (!(pFileData = (byte *)pPackage->getFile(filename, &fileSize))) {
+ BS_LOG_ERRORLN("File \"%s\" could not be loaded.", filename.c_str());
+ return;
+ }
+
+ // Bildeigenschaften bestimmen
+ int pitch;
+ if (!PNGLoader::imageProperties(pFileData, fileSize, _width, _height)) {
+ BS_LOG_ERRORLN("Could not read image properties.");
+ return;
+ }
+
+ // Das Bild dekomprimieren
+ byte *pUncompressedData;
+ if (!PNGLoader::decodeImage(pFileData, fileSize, pUncompressedData, _width, _height, pitch)) {
+ BS_LOG_ERRORLN("Could not decode image.");
+ return;
+ }
+
+ // Dateidaten freigeben
+ delete[] pFileData;
+
+ _imageDataPtr = (uint *)pUncompressedData;
+
+ result = true;
+ return;
+}
+
+SWImage::~SWImage() {
+ delete[] _imageDataPtr;
+}
+
+
+bool SWImage::blit(int posX, int posY,
+ int flipping,
+ Common::Rect *pPartRect,
+ uint color,
+ int width, int height) {
+ BS_LOG_ERRORLN("Blit() is not supported.");
+ return false;
+}
+
+bool SWImage::fill(const Common::Rect *pFillRect, uint color) {
+ BS_LOG_ERRORLN("Fill() is not supported.");
+ return false;
+}
+
+bool SWImage::setContent(const byte *pixeldata, uint size, uint offset, uint stride) {
+ BS_LOG_ERRORLN("SetContent() is not supported.");
+ return false;
+}
+
+uint SWImage::getPixel(int x, int y) {
+ BS_ASSERT(x >= 0 && x < _width);
+ BS_ASSERT(y >= 0 && y < _height);
+
+ return _imageDataPtr[_width * y + x];
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/gfx/image/swimage.h b/engines/sword25/gfx/image/swimage.h
new file mode 100644
index 0000000000..a914c4f41f
--- /dev/null
+++ b/engines/sword25/gfx/image/swimage.h
@@ -0,0 +1,99 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#ifndef SWORD25_SWIMAGE_H
+#define SWORD25_SWIMAGE_H
+
+#include "sword25/kernel/common.h"
+#include "sword25/gfx/image/image.h"
+#include "sword25/gfx/graphicengine.h"
+
+
+namespace Sword25 {
+
+class SWImage : public Image {
+public:
+ SWImage(const Common::String &filename, bool &result);
+ virtual ~SWImage();
+
+ virtual int getWidth() const {
+ return _width;
+ }
+ virtual int getHeight() const {
+ return _height;
+ }
+ virtual GraphicEngine::COLOR_FORMATS getColorFormat() const {
+ return GraphicEngine::CF_ARGB32;
+ }
+
+ virtual bool blit(int posX = 0, int posY = 0,
+ int flipping = Image::FLIP_NONE,
+ Common::Rect *pPartRect = NULL,
+ uint color = BS_ARGB(255, 255, 255, 255),
+ int width = -1, int height = -1);
+ virtual bool fill(const Common::Rect *fillRectPtr, uint color);
+ virtual bool setContent(const byte *pixeldata, uint size, uint offset, uint stride);
+ virtual uint getPixel(int x, int y);
+
+ virtual bool isBlitSource() const {
+ return false;
+ }
+ virtual bool isBlitTarget() const {
+ return false;
+ }
+ virtual bool isScalingAllowed() const {
+ return false;
+ }
+ virtual bool isFillingAllowed() const {
+ return false;
+ }
+ virtual bool isAlphaAllowed() const {
+ return false;
+ }
+ virtual bool isColorModulationAllowed() const {
+ return false;
+ }
+ virtual bool isSetContentAllowed() const {
+ return false;
+ }
+private:
+ uint *_imageDataPtr;
+
+ int _width;
+ int _height;
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/gfx/image/vectorimage.cpp b/engines/sword25/gfx/image/vectorimage.cpp
new file mode 100644
index 0000000000..5c15c4771a
--- /dev/null
+++ b/engines/sword25/gfx/image/vectorimage.cpp
@@ -0,0 +1,636 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "sword25/gfx/image/art.h"
+#include "sword25/gfx/image/vectorimage.h"
+#include "sword25/gfx/image/renderedimage.h"
+
+#include "graphics/colormasks.h"
+
+namespace Sword25 {
+
+#define BS_LOG_PREFIX "VECTORIMAGE"
+
+#define BEZSMOOTHNESS 0.5
+
+// -----------------------------------------------------------------------------
+// SWF Datentypen
+// -----------------------------------------------------------------------------
+
+// -----------------------------------------------------------------------------
+// Bitstream Hilfsklasse
+// -----------------------------------------------------------------------------
+// Das Parsen von SWF-Dateien erfordert sowohl bitweises Auslesen als auch an
+// Bytegrenzen ausgerichtetes Lesen.
+// Diese Klasse ist speziell dafür ausgestattet.
+// -----------------------------------------------------------------------------
+
+class VectorImage::SWFBitStream {
+public:
+ SWFBitStream(const byte *pData, uint dataSize) :
+ m_Pos(pData), m_End(pData + dataSize), m_WordMask(0)
+ {}
+
+ inline uint32 getBits(uint bitCount) {
+ if (bitCount == 0 || bitCount > 32) {
+ error("SWFBitStream::GetBits() must read at least 1 and at most 32 bits at a time");
+ }
+
+ uint32 value = 0;
+ while (bitCount) {
+ if (m_WordMask == 0)
+ flushByte();
+
+ value <<= 1;
+ value |= ((m_Word & m_WordMask) != 0) ? 1 : 0;
+ m_WordMask >>= 1;
+
+ --bitCount;
+ }
+
+ return value;
+ }
+
+ inline int32 getSignedBits(uint bitCount) {
+ // Bits einlesen
+ uint32 temp = getBits(bitCount);
+
+ // Falls das Sign-Bit gesetzt ist, den Rest des Rückgabewertes mit 1-Bits auffüllen (Sign Extension)
+ if (temp & 1 << (bitCount - 1))
+ return (0xffffffff << bitCount) | temp;
+ else
+ return temp;
+ }
+
+ inline uint32 getUInt32() {
+ uint32 byte1 = getByte();
+ uint32 byte2 = getByte();
+ uint32 byte3 = getByte();
+ uint32 byte4 = getByte();
+
+ return byte1 | (byte2 << 8) | (byte3 << 16) | (byte4 << 24);
+ }
+
+ inline uint16 getUInt16() {
+ uint32 byte1 = getByte();
+ uint32 byte2 = getByte();
+
+ return byte1 | (byte2 << 8);
+ }
+
+ inline byte getByte() {
+ flushByte();
+ byte value = m_Word;
+ m_WordMask = 0;
+ flushByte();
+
+ return value;
+ }
+
+ inline void flushByte() {
+ if (m_WordMask != 128) {
+ if (m_Pos >= m_End) {
+ error("Attempted to read past end of file");
+ } else {
+ m_Word = *m_Pos++;
+ m_WordMask = 128;
+ }
+ }
+ }
+
+ inline void skipBytes(uint skipLength) {
+ flushByte();
+ if (m_Pos + skipLength >= m_End) {
+ error("Attempted to read past end of file");
+ } else {
+ m_Pos += skipLength;
+ m_Word = *(m_Pos - 1);
+ }
+ }
+
+private:
+ const byte *m_Pos;
+ const byte *m_End;
+
+ byte m_Word;
+ uint m_WordMask;
+};
+
+
+// -----------------------------------------------------------------------------
+// Konstanten und Hilfsfunktionen
+// -----------------------------------------------------------------------------
+
+namespace {
+// -----------------------------------------------------------------------------
+// Konstanten
+// -----------------------------------------------------------------------------
+
+const uint32 MAX_ACCEPTED_FLASH_VERSION = 3; // Die höchste Flash-Dateiversion, die vom Lader akzeptiert wird
+
+
+// -----------------------------------------------------------------------------
+// Konvertiert SWF-Rechteckdaten in einem Bitstrom in Common::Rect-Objekte
+// -----------------------------------------------------------------------------
+
+Common::Rect flashRectToBSRect(VectorImage::SWFBitStream &bs) {
+ bs.flushByte();
+
+ // Feststellen mit wie vielen Bits die einzelnen Komponenten kodiert sind
+ uint32 bitsPerValue = bs.getBits(5);
+
+ // Die einzelnen Komponenten einlesen
+ int32 xMin = bs.getSignedBits(bitsPerValue);
+ int32 xMax = bs.getSignedBits(bitsPerValue);
+ int32 yMin = bs.getSignedBits(bitsPerValue);
+ int32 yMax = bs.getSignedBits(bitsPerValue);
+
+ return Common::Rect(xMin, yMin, xMax + 1, yMax + 1);
+}
+
+// -----------------------------------------------------------------------------
+// Berechnet die Bounding-Box eines BS_VectorImageElement
+// -----------------------------------------------------------------------------
+
+Common::Rect CalculateBoundingBox(const VectorImageElement &vectorImageElement) {
+ double x0 = 0.0, y0 = 0.0, x1 = 0.0, y1 = 0.0;
+
+ for (int j = vectorImageElement.getPathCount() - 1; j >= 0; j--) {
+ ArtBpath *bez = vectorImageElement.getPathInfo(j).getVec();
+ ArtVpath *vec = art_bez_path_to_vec(bez, 0.5);
+
+ if (vec[0].code == ART_END) {
+ continue;
+ } else {
+ x0 = x1 = vec[0].x;
+ y0 = y1 = vec[0].y;
+ for (int i = 1; vec[i].code != ART_END; i++) {
+ if (vec[i].x < x0) x0 = vec[i].x;
+ if (vec[i].x > x1) x1 = vec[i].x;
+ if (vec[i].y < y0) y0 = vec[i].y;
+ if (vec[i].y > y1) y1 = vec[i].y;
+ }
+ }
+ free(vec);
+ }
+
+ return Common::Rect(static_cast<int>(x0), static_cast<int>(y0), static_cast<int>(x1) + 1, static_cast<int>(y1) + 1);
+}
+
+}
+
+
+// -----------------------------------------------------------------------------
+// Konstruktion
+// -----------------------------------------------------------------------------
+
+VectorImage::VectorImage(const byte *pFileData, uint fileSize, bool &success, const Common::String &fname) : _pixelData(0), _fname(fname) {
+ success = false;
+
+ // Bitstream-Objekt erzeugen
+ // Im Folgenden werden die Dateidaten aus diesem ausgelesen.
+ SWFBitStream bs(pFileData, fileSize);
+
+ // SWF-Signatur überprüfen
+ uint32 signature[3];
+ signature[0] = bs.getByte();
+ signature[1] = bs.getByte();
+ signature[2] = bs.getByte();
+ if (signature[0] != 'F' ||
+ signature[1] != 'W' ||
+ signature[2] != 'S') {
+ BS_LOG_ERRORLN("File is not a valid SWF-file");
+ return;
+ }
+
+ // Versionsangabe überprüfen
+ uint32 version = bs.getByte();
+ if (version > MAX_ACCEPTED_FLASH_VERSION) {
+ BS_LOG_ERRORLN("File is of version %d. Highest accepted version is %d.", version, MAX_ACCEPTED_FLASH_VERSION);
+ return;
+ }
+
+ // Dateigröße auslesen und mit der tatsächlichen Größe vergleichen
+ uint32 storedFileSize = bs.getUInt32();
+ if (storedFileSize != fileSize) {
+ BS_LOG_ERRORLN("File is not a valid SWF-file");
+ return;
+ }
+
+ // SWF-Maße auslesen
+ Common::Rect movieRect = flashRectToBSRect(bs);
+
+ // Framerate und Frameanzahl auslesen
+ /* uint32 frameRate = */
+ bs.getUInt16();
+ /* uint32 frameCount = */
+ bs.getUInt16();
+
+ // Tags parsen
+ // Da wir uns nur für das erste DefineShape-Tag interessieren
+ bool keepParsing = true;
+ while (keepParsing) {
+ // Tags beginnen immer an Bytegrenzen
+ bs.flushByte();
+
+ // Tagtyp und Länge auslesen
+ uint16 tagTypeAndLength = bs.getUInt16();
+ uint32 tagType = tagTypeAndLength >> 6;
+ uint32 tagLength = tagTypeAndLength & 0x3f;
+ if (tagLength == 0x3f)
+ tagLength = bs.getUInt32();
+
+ switch (tagType) {
+ case 2:
+ // DefineShape
+ success = parseDefineShape(2, bs);
+ return;
+ case 22:
+ // DefineShape2
+ success = parseDefineShape(2, bs);
+ return;
+ case 32:
+ success = parseDefineShape(3, bs);
+ return;
+ default:
+ // Unbekannte Tags ignorieren
+ bs.skipBytes(tagLength);
+ }
+ }
+
+ // Die Ausführung darf nicht an dieser Stelle ankommen: Entweder es wird ein Shape gefunden, dann wird die Funktion mit vorher verlassen, oder
+ // es wird keines gefunden, dann tritt eine Exception auf sobald über das Ende der Datei hinaus gelesen wird.
+ BS_ASSERT(false);
+}
+
+VectorImage::~VectorImage() {
+ for (int j = _elements.size() - 1; j >= 0; j--)
+ for (int i = _elements[j].getPathCount() - 1; i >= 0; i--)
+ if (_elements[j].getPathInfo(i).getVec())
+ free(_elements[j].getPathInfo(i).getVec());
+
+ if (_pixelData)
+ free(_pixelData);
+}
+
+
+ArtBpath *ensureBezStorage(ArtBpath *bez, int nodes, int *allocated) {
+ if (*allocated <= nodes) {
+ (*allocated) += 20;
+
+ return art_renew(bez, ArtBpath, *allocated);
+ }
+
+ return bez;
+}
+
+ArtBpath *VectorImage::storeBez(ArtBpath *bez, int lineStyle, int fillStyle0, int fillStyle1, int *bezNodes, int *bezAllocated) {
+ (*bezNodes)++;
+
+ bez = ensureBezStorage(bez, *bezNodes, bezAllocated);
+ bez[*bezNodes].code = ART_END;
+
+ ArtBpath *bez1 = art_new(ArtBpath, *bezNodes + 1);
+
+ for (int i = 0; i <= *bezNodes; i++)
+ bez1[i] = bez[i];
+
+ _elements.back()._pathInfos.push_back(VectorPathInfo(bez1, *bezNodes, lineStyle, fillStyle0, fillStyle1));
+
+ return bez;
+}
+
+bool VectorImage::parseDefineShape(uint shapeType, SWFBitStream &bs) {
+ /*uint32 shapeID = */bs.getUInt16();
+
+ // Bounding Box auslesen
+ _boundingBox = flashRectToBSRect(bs);
+
+ // Erstes Image-Element erzeugen
+ _elements.resize(1);
+
+ // Styles einlesen
+ uint numFillBits;
+ uint numLineBits;
+ if (!parseStyles(shapeType, bs, numFillBits, numLineBits))
+ return false;
+
+ uint lineStyle = 0;
+ uint fillStyle0 = 0;
+ uint fillStyle1 = 0;
+
+ // Shaperecord parsen
+ // ------------------
+
+ double curX = 0;
+ double curY = 0;
+ int bezNodes = 0;
+ int bezAllocated = 10;
+ ArtBpath *bez = art_new(ArtBpath, bezAllocated);
+
+ bool endOfShapeDiscovered = false;
+ while (!endOfShapeDiscovered) {
+ uint32 typeFlag = bs.getBits(1);
+
+ // Non-Edge Record
+ if (typeFlag == 0) {
+ // Feststellen welche Parameter gesetzt werden
+ uint32 stateNewStyles = bs.getBits(1);
+ uint32 stateLineStyle = bs.getBits(1);
+ uint32 stateFillStyle1 = bs.getBits(1);
+ uint32 stateFillStyle0 = bs.getBits(1);
+ uint32 stateMoveTo = bs.getBits(1);
+
+ uint prevLineStyle = lineStyle;
+ uint prevFillStyle0 = fillStyle0;
+ uint prevFillStyle1 = fillStyle1;
+
+ // End der Shape-Definition erreicht?
+ if (!stateNewStyles && !stateLineStyle && !stateFillStyle0 && !stateFillStyle1 && !stateMoveTo) {
+ endOfShapeDiscovered = true;
+ // Parameter dekodieren
+ } else {
+ if (stateMoveTo) {
+ uint32 moveToBits = bs.getBits(5);
+ curX = bs.getSignedBits(moveToBits);
+ curY = bs.getSignedBits(moveToBits);
+ }
+
+ if (stateFillStyle0) {
+ if (numFillBits > 0)
+ fillStyle0 = bs.getBits(numFillBits);
+ else
+ fillStyle0 = 0;
+ }
+
+ if (stateFillStyle1) {
+ if (numFillBits > 0)
+ fillStyle1 = bs.getBits(numFillBits);
+ else
+ fillStyle1 = 0;
+ }
+
+ if (stateLineStyle) {
+ if (numLineBits)
+ lineStyle = bs.getBits(numLineBits);
+ else
+ numLineBits = 0;
+ }
+
+ // Ein neuen Pfad erzeugen, es sei denn, es wurden nur neue Styles definiert
+ if (stateLineStyle || stateFillStyle0 || stateFillStyle1 || stateMoveTo) {
+ // Store previous curve if any
+ if (bezNodes) {
+ bez = storeBez(bez, prevLineStyle, prevFillStyle0, prevFillStyle1, &bezNodes, &bezAllocated);
+ }
+
+ // Start new curve
+ bez = ensureBezStorage(bez, 1, &bezAllocated);
+ bez[0].code = ART_MOVETO_OPEN;
+ bez[0].x3 = curX;
+ bez[0].y3 = curY;
+ bezNodes = 0;
+ }
+
+ if (stateNewStyles) {
+ // An dieser Stelle werden in Flash die alten Style-Definitionen verworfen und mit den neuen überschrieben.
+ // Es wird ein neues Element begonnen.
+ _elements.resize(_elements.size() + 1);
+ if (!parseStyles(shapeType, bs, numFillBits, numLineBits))
+ return false;
+ }
+ }
+ } else {
+ // Edge Record
+ uint32 edgeFlag = bs.getBits(1);
+ uint32 numBits = bs.getBits(4) + 2;
+
+ // Curved edge
+ if (edgeFlag == 0) {
+ double controlDeltaX = bs.getSignedBits(numBits);
+ double controlDeltaY = bs.getSignedBits(numBits);
+ double anchorDeltaX = bs.getSignedBits(numBits);
+ double anchorDeltaY = bs.getSignedBits(numBits);
+
+ double controlX = curX + controlDeltaX;
+ double controlY = curY + controlDeltaY;
+ double newX = controlX + anchorDeltaX;
+ double newY = controlY + anchorDeltaY;
+
+#define WEIGHT (2.0/3.0)
+
+ bezNodes++;
+ bez = ensureBezStorage(bez, bezNodes, &bezAllocated);
+ bez[bezNodes].code = ART_CURVETO;
+ bez[bezNodes].x1 = WEIGHT * controlX + (1 - WEIGHT) * curX;
+ bez[bezNodes].y1 = WEIGHT * controlY + (1 - WEIGHT) * curY;
+ bez[bezNodes].x2 = WEIGHT * controlX + (1 - WEIGHT) * newX;
+ bez[bezNodes].y2 = WEIGHT * controlY + (1 - WEIGHT) * newY;
+ bez[bezNodes].x3 = newX;
+ bez[bezNodes].y3 = newY;
+
+ curX = newX;
+ curY = newY;
+ } else {
+ // Staight edge
+ int32 deltaX = 0;
+ int32 deltaY = 0;
+
+ uint32 generalLineFlag = bs.getBits(1);
+ if (generalLineFlag) {
+ deltaX = bs.getSignedBits(numBits);
+ deltaY = bs.getSignedBits(numBits);
+ } else {
+ uint32 vertLineFlag = bs.getBits(1);
+ if (vertLineFlag)
+ deltaY = bs.getSignedBits(numBits);
+ else
+ deltaX = bs.getSignedBits(numBits);
+ }
+
+ curX += deltaX;
+ curY += deltaY;
+
+ bezNodes++;
+ bez = ensureBezStorage(bez, bezNodes, &bezAllocated);
+ bez[bezNodes].code = ART_LINETO;
+ bez[bezNodes].x3 = curX;
+ bez[bezNodes].y3 = curY;
+ }
+ }
+ }
+
+ // Store last curve
+ if (bezNodes)
+ bez = storeBez(bez, lineStyle, fillStyle0, fillStyle1, &bezNodes, &bezAllocated);
+
+ free(bez);
+
+ // Bounding-Boxes der einzelnen Elemente berechnen
+ Common::Array<VectorImageElement>::iterator it = _elements.begin();
+ for (; it != _elements.end(); ++it)
+ it->_boundingBox = CalculateBoundingBox(*it);
+
+ return true;
+}
+
+
+// -----------------------------------------------------------------------------
+
+bool VectorImage::parseStyles(uint shapeType, SWFBitStream &bs, uint &numFillBits, uint &numLineBits) {
+ bs.flushByte();
+
+ // Fillstyles parsen
+ // -----------------
+
+ // Anzahl an Fillstyles bestimmen
+ uint fillStyleCount = bs.getByte();
+ if (fillStyleCount == 0xff)
+ fillStyleCount = bs.getUInt16();
+
+ // Alle Fillstyles einlesen, falls ein Fillstyle mit Typ != 0 gefunden wird, wird das Parsen abgebrochen.
+ // Es wird nur "solid fill" (Typ 0) unterstützt.
+ _elements.back()._fillStyles.reserve(fillStyleCount);
+ for (uint i = 0; i < fillStyleCount; ++i) {
+ byte type = bs.getByte();
+ uint32 color;
+ byte r = bs.getByte();
+ byte g = bs.getByte();
+ byte b = bs.getByte();
+ byte a = 0xff;
+
+ if (shapeType == 3)
+ a = bs.getByte();
+
+ color = Graphics::ARGBToColor<Graphics::ColorMasks<8888> >(a, r, g, b);
+
+ if (type != 0)
+ return false;
+
+ _elements.back()._fillStyles.push_back(color);
+ }
+
+ // Linestyles parsen
+ // -----------------
+
+ // Anzahl an Linestyles bestimmen
+ uint lineStyleCount = bs.getByte();
+ if (lineStyleCount == 0xff)
+ lineStyleCount = bs.getUInt16();
+
+ // Alle Linestyles einlesen
+ _elements.back()._lineStyles.reserve(lineStyleCount);
+ for (uint i = 0; i < lineStyleCount; ++i) {
+ double width = bs.getUInt16();
+ uint32 color;
+ byte r = bs.getByte();
+ byte g = bs.getByte();
+ byte b = bs.getByte();
+ byte a = 0xff;
+
+ if (shapeType == 3)
+ a = bs.getByte();
+
+ color = Graphics::ARGBToColor<Graphics::ColorMasks<8888> >(a, r, g, b);
+
+ _elements.back()._lineStyles.push_back(VectorImageElement::LineStyleType(width, color));
+ }
+
+ // Bitbreite für die folgenden Styleindizes auslesen
+ numFillBits = bs.getBits(4);
+ numLineBits = bs.getBits(4);
+
+ return true;
+}
+
+
+// -----------------------------------------------------------------------------
+
+bool VectorImage::fill(const Common::Rect *pFillRect, uint color) {
+ BS_LOG_ERRORLN("Fill() is not supported.");
+ return false;
+}
+
+
+// -----------------------------------------------------------------------------
+
+uint VectorImage::getPixel(int x, int y) {
+ BS_LOG_ERRORLN("GetPixel() is not supported. Returning black.");
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+bool VectorImage::setContent(const byte *pixeldata, uint size, uint offset, uint stride) {
+ BS_LOG_ERRORLN("SetContent() is not supported.");
+ return 0;
+}
+
+bool VectorImage::blit(int posX, int posY,
+ int flipping,
+ Common::Rect *pPartRect,
+ uint color,
+ int width, int height) {
+ static VectorImage *oldThis = 0;
+ static int oldWidth = -2;
+ static int oldHeight = -2;
+
+ // Falls Breite oder Höhe 0 sind, muss nichts dargestellt werden.
+ if (width == 0 || height == 0)
+ return true;
+
+ // Feststellen, ob das alte Bild im Cache nicht wiederbenutzt werden kann und neu Berechnet werden muss
+ if (!(oldThis == this && oldWidth == width && oldHeight == height)) {
+ render(width, height);
+
+ oldThis = this;
+ oldHeight = height;
+ oldWidth = width;
+ }
+
+ RenderedImage *rend = new RenderedImage();
+
+ rend->replaceContent(_pixelData, width, height);
+ rend->blit(posX, posY, flipping, pPartRect, color, width, height);
+
+ delete rend;
+
+ return true;
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/gfx/image/vectorimage.h b/engines/sword25/gfx/image/vectorimage.h
new file mode 100644
index 0000000000..3477463b43
--- /dev/null
+++ b/engines/sword25/gfx/image/vectorimage.h
@@ -0,0 +1,237 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#ifndef SWORD25_VECTORIMAGE_H
+#define SWORD25_VECTORIMAGE_H
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "sword25/kernel/common.h"
+#include "sword25/gfx/image/image.h"
+#include "common/rect.h"
+
+#include "art.h"
+
+namespace Sword25 {
+
+class VectorImage;
+
+/**
+ @brief Pfadinformationen zu BS_VectorImageElement Objekten
+
+ Jedes BS_VectorImageElement besteht aus Kantenzügen, oder auch Pfaden. Jeder dieser Pfad hat Eigenschaften, die in Objekten diesen Typs
+ gespeichert werden.
+*/
+
+class VectorPathInfo {
+public:
+ VectorPathInfo(ArtBpath *vec, int len, uint lineStyle, uint fillStyle0, uint fillStyle1) :
+ _vec(vec), _lineStyle(lineStyle), _fillStyle0(fillStyle0), _fillStyle1(fillStyle1), _len(len) {}
+
+ VectorPathInfo() {
+ _lineStyle = _fillStyle0 = _fillStyle1 = _len = 0;
+ _vec = 0;
+ }
+
+ ArtBpath *getVec() const {
+ return _vec;
+ }
+ int getVecLen() const {
+ return _len;
+ }
+ uint getLineStyle() const {
+ return _lineStyle;
+ }
+ uint getFillStyle0() const {
+ return _fillStyle0;
+ }
+ uint getFillStyle1() const {
+ return _fillStyle1;
+ }
+
+private:
+ ArtBpath *_vec;
+ uint _lineStyle;
+ uint _fillStyle0;
+ uint _fillStyle1;
+ uint _len;
+};
+
+/**
+ @brief Ein Element eines Vektorbild. Ein BS_VectorImage besteht aus diesen Elementen, die jeweils einen Teil der Graphik definieren.
+ Werden alle Elemente eines Vektorbildes übereinandergelegt, ergibt sich das komplette Bild.
+*/
+class VectorImageElement {
+ friend class VectorImage;
+public:
+ uint getPathCount() const {
+ return _pathInfos.size();
+ }
+ const VectorPathInfo &getPathInfo(uint pathNr) const {
+ BS_ASSERT(pathNr < getPathCount());
+ return _pathInfos[pathNr];
+ }
+
+ double getLineStyleWidth(uint lineStyle) const {
+ BS_ASSERT(lineStyle < _lineStyles.size());
+ return _lineStyles[lineStyle].width;
+ }
+
+ uint getLineStyleCount() const {
+ return _lineStyles.size();
+ }
+
+ uint32 getLineStyleColor(uint lineStyle) const {
+ BS_ASSERT(lineStyle < _lineStyles.size());
+ return _lineStyles[lineStyle].color;
+ }
+
+ uint getFillStyleCount() const {
+ return _fillStyles.size();
+ }
+
+ uint32 getFillStyleColor(uint fillStyle) const {
+ BS_ASSERT(fillStyle < _fillStyles.size());
+ return _fillStyles[fillStyle];
+ }
+
+ const Common::Rect &getBoundingBox() const {
+ return _boundingBox;
+ }
+
+private:
+ struct LineStyleType {
+ LineStyleType(double width_, uint32 color_) : width(width_), color(color_) {}
+ LineStyleType() {
+ width = 0;
+ color = 0;
+ }
+ double width;
+ uint32 color;
+ };
+
+ Common::Array<VectorPathInfo> _pathInfos;
+ Common::Array<LineStyleType> _lineStyles;
+ Common::Array<uint32> _fillStyles;
+ Common::Rect _boundingBox;
+};
+
+
+/**
+ @brief Eine Vektorgraphik
+
+ Objekte dieser Klasse enthalten die Informationen eines SWF-Shapes.
+*/
+
+class VectorImage : public Image {
+public:
+ VectorImage(const byte *pFileData, uint fileSize, bool &success, const Common::String &fname);
+ ~VectorImage();
+
+ uint getElementCount() const {
+ return _elements.size();
+ }
+ const VectorImageElement &getElement(uint elementNr) const {
+ BS_ASSERT(elementNr < _elements.size());
+ return _elements[elementNr];
+ }
+ const Common::Rect &getBoundingBox() const {
+ return _boundingBox;
+ }
+
+ //
+ // Die abstrakten Methoden von BS_Image
+ //
+ virtual int getWidth() const {
+ return _boundingBox.width();
+ }
+ virtual int getHeight() const {
+ return _boundingBox.height();
+ }
+ virtual GraphicEngine::COLOR_FORMATS getColorFormat() const {
+ return GraphicEngine::CF_ARGB32;
+ }
+ virtual bool fill(const Common::Rect *pFillRect = 0, uint color = BS_RGB(0, 0, 0));
+
+ void render(int width, int height);
+
+ virtual uint getPixel(int x, int y);
+ virtual bool isBlitSource() const {
+ return true;
+ }
+ virtual bool isBlitTarget() const {
+ return false;
+ }
+ virtual bool isScalingAllowed() const {
+ return true;
+ }
+ virtual bool isFillingAllowed() const {
+ return false;
+ }
+ virtual bool isAlphaAllowed() const {
+ return true;
+ }
+ virtual bool isColorModulationAllowed() const {
+ return true;
+ }
+ virtual bool isSetContentAllowed() const {
+ return false;
+ }
+ virtual bool setContent(const byte *pixeldata, uint size, uint offset, uint stride);
+ virtual bool blit(int posX = 0, int posY = 0,
+ int flipping = FLIP_NONE,
+ Common::Rect *pPartRect = NULL,
+ uint color = BS_ARGB(255, 255, 255, 255),
+ int width = -1, int height = -1);
+
+ class SWFBitStream;
+
+private:
+ bool parseDefineShape(uint shapeType, SWFBitStream &bs);
+ bool parseStyles(uint shapeType, SWFBitStream &bs, uint &numFillBits, uint &numLineBits);
+
+ ArtBpath *storeBez(ArtBpath *bez, int lineStyle, int fillStyle0, int fillStyle1, int *bezNodes, int *bezAllocated);
+ Common::Array<VectorImageElement> _elements;
+ Common::Rect _boundingBox;
+
+ byte *_pixelData;
+
+ Common::String _fname;
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/gfx/image/vectorimagerenderer.cpp b/engines/sword25/gfx/image/vectorimagerenderer.cpp
new file mode 100644
index 0000000000..43ac8683ac
--- /dev/null
+++ b/engines/sword25/gfx/image/vectorimagerenderer.cpp
@@ -0,0 +1,461 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * This code contains portions of Libart_LGPL - library of basic graphic primitives
+ *
+ * Copyright (c) 1998 Raph Levien
+ *
+ * Licensed under GNU LGPL v2
+ *
+ */
+
+/*
+ * This code contains portions of Swfdec
+ *
+ * Copyright (c) 2004-2006 David Schleef <ds@schleef.org>
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#include "art.h"
+
+#include "sword25/gfx/image/vectorimage.h"
+#include "graphics/colormasks.h"
+
+namespace Sword25 {
+
+void art_rgb_fill_run1(art_u8 *buf, art_u8 r, art_u8 g, art_u8 b, int n) {
+ int i;
+
+ if (r == g && g == b && r == 255) {
+ memset(buf, g, n + n + n + n);
+ } else {
+ uint32 *alt = (uint32 *)buf;
+ uint32 color = Graphics::ARGBToColor<Graphics::ColorMasks<8888> >(0xff, r, g, b);
+
+ for (i = 0; i < n; i++)
+ *alt++ = color;
+ }
+}
+
+void art_rgb_run_alpha1(art_u8 *buf, art_u8 r, art_u8 g, art_u8 b, int alpha, int n) {
+ int i;
+ int v;
+
+ for (i = 0; i < n; i++) {
+ v = *buf;
+ *buf++ = v + (((b - v) * alpha + 0x80) >> 8);
+ v = *buf;
+ *buf++ = v + (((g - v) * alpha + 0x80) >> 8);
+ v = *buf;
+ *buf++ = v + (((r - v) * alpha + 0x80) >> 8);
+ v = *buf;
+ *buf++ = MIN(v + alpha, 0xff);
+ }
+}
+
+typedef struct _ArtRgbSVPAlphaData ArtRgbSVPAlphaData;
+
+struct _ArtRgbSVPAlphaData {
+ int alphatab[256];
+ art_u8 r, g, b, alpha;
+ art_u8 *buf;
+ int rowstride;
+ int x0, x1;
+};
+
+static void art_rgb_svp_alpha_callback1(void *callback_data, int y,
+ int start, ArtSVPRenderAAStep *steps, int n_steps) {
+ ArtRgbSVPAlphaData *data = (ArtRgbSVPAlphaData *)callback_data;
+ art_u8 *linebuf;
+ int run_x0, run_x1;
+ art_u32 running_sum = start;
+ int x0, x1;
+ int k;
+ art_u8 r, g, b;
+ int *alphatab;
+ int alpha;
+
+ linebuf = data->buf;
+ x0 = data->x0;
+ x1 = data->x1;
+
+ r = data->r;
+ g = data->g;
+ b = data->b;
+ alphatab = data->alphatab;
+
+ if (n_steps > 0) {
+ run_x1 = steps[0].x;
+ if (run_x1 > x0) {
+ alpha = (running_sum >> 16) & 0xff;
+ if (alpha)
+ art_rgb_run_alpha1(linebuf, r, g, b, alphatab[alpha], run_x1 - x0);
+ }
+
+ for (k = 0; k < n_steps - 1; k++) {
+ running_sum += steps[k].delta;
+ run_x0 = run_x1;
+ run_x1 = steps[k + 1].x;
+ if (run_x1 > run_x0) {
+ alpha = (running_sum >> 16) & 0xff;
+ if (alpha)
+ art_rgb_run_alpha1(linebuf + (run_x0 - x0) * 4, r, g, b, alphatab[alpha], run_x1 - run_x0);
+ }
+ }
+ running_sum += steps[k].delta;
+ if (x1 > run_x1) {
+ alpha = (running_sum >> 16) & 0xff;
+ if (alpha)
+ art_rgb_run_alpha1(linebuf + (run_x1 - x0) * 4, r, g, b, alphatab[alpha], x1 - run_x1);
+ }
+ } else {
+ alpha = (running_sum >> 16) & 0xff;
+ if (alpha)
+ art_rgb_run_alpha1(linebuf, r, g, b, alphatab[alpha], x1 - x0);
+ }
+
+ data->buf += data->rowstride;
+}
+
+static void art_rgb_svp_alpha_opaque_callback1(void *callback_data, int y,
+ int start,
+ ArtSVPRenderAAStep *steps, int n_steps) {
+ ArtRgbSVPAlphaData *data = (ArtRgbSVPAlphaData *)callback_data;
+ art_u8 *linebuf;
+ int run_x0, run_x1;
+ art_u32 running_sum = start;
+ int x0, x1;
+ int k;
+ art_u8 r, g, b;
+ int *alphatab;
+ int alpha;
+
+ linebuf = data->buf;
+ x0 = data->x0;
+ x1 = data->x1;
+
+ r = data->r;
+ g = data->g;
+ b = data->b;
+ alphatab = data->alphatab;
+
+ if (n_steps > 0) {
+ run_x1 = steps[0].x;
+ if (run_x1 > x0) {
+ alpha = running_sum >> 16;
+ if (alpha) {
+ if (alpha >= 255)
+ art_rgb_fill_run1(linebuf, r, g, b, run_x1 - x0);
+ else
+ art_rgb_run_alpha1(linebuf, r, g, b, alphatab[alpha], run_x1 - x0);
+ }
+ }
+
+ for (k = 0; k < n_steps - 1; k++) {
+ running_sum += steps[k].delta;
+ run_x0 = run_x1;
+ run_x1 = steps[k + 1].x;
+ if (run_x1 > run_x0) {
+ alpha = running_sum >> 16;
+ if (alpha) {
+ if (alpha >= 255)
+ art_rgb_fill_run1(linebuf + (run_x0 - x0) * 4, r, g, b, run_x1 - run_x0);
+ else
+ art_rgb_run_alpha1(linebuf + (run_x0 - x0) * 4, r, g, b, alphatab[alpha], run_x1 - run_x0);
+ }
+ }
+ }
+ running_sum += steps[k].delta;
+ if (x1 > run_x1) {
+ alpha = running_sum >> 16;
+ if (alpha) {
+ if (alpha >= 255)
+ art_rgb_fill_run1(linebuf + (run_x1 - x0) * 4, r, g, b, x1 - run_x1);
+ else
+ art_rgb_run_alpha1(linebuf + (run_x1 - x0) * 4, r, g, b, alphatab[alpha], x1 - run_x1);
+ }
+ }
+ } else {
+ alpha = running_sum >> 16;
+ if (alpha) {
+ if (alpha >= 255)
+ art_rgb_fill_run1(linebuf, r, g, b, x1 - x0);
+ else
+ art_rgb_run_alpha1(linebuf, r, g, b, alphatab[alpha], x1 - x0);
+ }
+ }
+
+ data->buf += data->rowstride;
+}
+
+void art_rgb_svp_alpha1(const ArtSVP *svp,
+ int x0, int y0, int x1, int y1,
+ uint32 color,
+ art_u8 *buf, int rowstride) {
+ ArtRgbSVPAlphaData data;
+ byte r, g, b, alpha;
+ int i;
+ int a, da;
+
+ Graphics::colorToARGB<Graphics::ColorMasks<8888> >(color, alpha, r, g, b);
+
+ data.r = r;
+ data.g = g;
+ data.b = b;
+ data.alpha = alpha;
+
+ a = 0x8000;
+ da = (alpha * 66051 + 0x80) >> 8; /* 66051 equals 2 ^ 32 / (255 * 255) */
+
+ for (i = 0; i < 256; i++) {
+ data.alphatab[i] = a >> 16;
+ a += da;
+ }
+
+ data.buf = buf;
+ data.rowstride = rowstride;
+ data.x0 = x0;
+ data.x1 = x1;
+ if (alpha == 255)
+ art_svp_render_aa(svp, x0, y0, x1, y1, art_rgb_svp_alpha_opaque_callback1, &data);
+ else
+ art_svp_render_aa(svp, x0, y0, x1, y1, art_rgb_svp_alpha_callback1, &data);
+}
+
+static int art_vpath_len(ArtVpath *a) {
+ int i = 0;
+ while (a[i].code != ART_END)
+ i++;
+ return i;
+}
+
+ArtVpath *art_vpath_cat(ArtVpath *a, ArtVpath *b) {
+ ArtVpath *dest;
+ ArtVpath *p;
+ int len_a, len_b;
+
+ len_a = art_vpath_len(a);
+ len_b = art_vpath_len(b);
+ dest = art_new(ArtVpath, len_a + len_b + 1);
+ p = dest;
+
+ for (int i = 0; i < len_a; i++)
+ *p++ = *a++;
+ for (int i = 0; i <= len_b; i++)
+ *p++ = *b++;
+
+ return dest;
+}
+
+void art_svp_make_convex(ArtSVP *svp) {
+ int i;
+
+ if (svp->n_segs > 0 && svp->segs[0].dir == 0) {
+ for (i = 0; i < svp->n_segs; i++) {
+ svp->segs[i].dir = !svp->segs[i].dir;
+ }
+ }
+}
+
+ArtVpath *art_vpath_reverse(ArtVpath *a) {
+ ArtVpath *dest;
+ ArtVpath it;
+ int len;
+ int state = 0;
+ int i;
+
+ len = art_vpath_len(a);
+ dest = art_new(ArtVpath, len + 1);
+
+ for (i = 0; i < len; i++) {
+ it = a[len - i - 1];
+ if (state) {
+ it.code = ART_LINETO;
+ } else {
+ it.code = ART_MOVETO_OPEN;
+ state = 1;
+ }
+ if (a[len - i - 1].code == ART_MOVETO ||
+ a[len - i - 1].code == ART_MOVETO_OPEN) {
+ state = 0;
+ }
+ dest[i] = it;
+ }
+ dest[len] = a[len];
+
+ return dest;
+}
+
+ArtVpath *art_vpath_reverse_free(ArtVpath *a) {
+ ArtVpath *dest;
+
+ dest = art_vpath_reverse(a);
+ free(a);
+
+ return dest;
+}
+
+void drawBez(ArtBpath *bez1, ArtBpath *bez2, art_u8 *buffer, int width, int height, int deltaX, int deltaY, double scaleX, double scaleY, double penWidth, unsigned int color) {
+ ArtVpath *vec = NULL;
+ ArtVpath *vec1 = NULL;
+ ArtVpath *vec2 = NULL;
+ ArtSVP *svp = NULL;
+
+#if 0
+ const char *codes[] = {"ART_MOVETO", "ART_MOVETO_OPEN", "ART_CURVETO", "ART_LINETO", "ART_END"};
+ for (int i = 0;; i++) {
+ printf(" bez[%d].code = %s;\n", i, codes[bez[i].code]);
+ if (bez[i].code == ART_END)
+ break;
+ if (bez[i].code == ART_CURVETO) {
+ printf(" bez[%d].x1 = %f; bez[%d].y1 = %f;\n", i, bez[i].x1, i, bez[i].y1);
+ printf(" bez[%d].x2 = %f; bez[%d].y2 = %f;\n", i, bez[i].x2, i, bez[i].y2);
+ }
+ printf(" bez[%d].x3 = %f; bez[%d].y3 = %f;\n", i, bez[i].x3, i, bez[i].y3);
+ }
+
+ printf(" drawBez(bez, buffer, 1.0, 1.0, %f, 0x%08x);\n", penWidth, color);
+#endif
+
+ // HACK: Some frames have green bounding boxes drawn.
+ // Perhaps they were used by original game artist Umriss
+ // We skip them just like the original
+ if (bez2 == 0 && color == Graphics::ARGBToColor<Graphics::ColorMasks<8888> >(0xff, 0x00, 0xff, 0x00)) {
+ return;
+ }
+
+ vec1 = art_bez_path_to_vec(bez1, 0.5);
+ if (bez2 != 0) {
+ vec2 = art_bez_path_to_vec(bez2, 0.5);
+ vec2 = art_vpath_reverse_free(vec2);
+ vec = art_vpath_cat(vec1, vec2);
+
+ free(vec1);
+ free(vec2);
+ } else {
+ vec = vec1;
+ }
+
+ int size = art_vpath_len(vec);
+ ArtVpath *vect = art_new(ArtVpath, size + 1);
+
+ int k;
+ for (k = 0; k < size; k++) {
+ vect[k].code = vec[k].code;
+ vect[k].x = (vec[k].x - deltaX) * scaleX;
+ vect[k].y = (vec[k].y - deltaY) * scaleY;
+ }
+ vect[k].code = ART_END;
+
+ if (bez2 == 0) { // Line drawing
+ svp = art_svp_vpath_stroke(vect, ART_PATH_STROKE_JOIN_ROUND, ART_PATH_STROKE_CAP_ROUND, penWidth, 1.0, 0.5);
+ } else {
+ svp = art_svp_from_vpath(vect);
+ art_svp_make_convex(svp);
+ }
+
+ art_rgb_svp_alpha1(svp, 0, 0, width, height, color, buffer, width * 4);
+
+ free(vect);
+ art_svp_free(svp);
+ free(vec);
+}
+
+void VectorImage::render(int width, int height) {
+ double scaleX = (width == - 1) ? 1 : static_cast<double>(width) / static_cast<double>(getWidth());
+ double scaleY = (height == - 1) ? 1 : static_cast<double>(height) / static_cast<double>(getHeight());
+
+ debug(3, "VectorImage::render(%d, %d) %s", width, height, _fname.c_str());
+
+ if (_pixelData)
+ free(_pixelData);
+
+ _pixelData = (byte *)malloc(width * height * 4);
+ memset(_pixelData, 0, width * height * 4);
+
+ for (uint e = 0; e < _elements.size(); e++) {
+
+ //// Draw shapes
+ for (uint s = 0; s < _elements[e].getFillStyleCount(); s++) {
+ int fill0len = 0;
+ int fill1len = 0;
+
+ // Count vector sizes in order to minimize memory
+ // fragmentation
+ for (uint p = 0; p < _elements[e].getPathCount(); p++) {
+ if (_elements[e].getPathInfo(p).getFillStyle0() == s + 1)
+ fill0len += _elements[e].getPathInfo(p).getVecLen();
+
+ if (_elements[e].getPathInfo(p).getFillStyle1() == s + 1)
+ fill1len += _elements[e].getPathInfo(p).getVecLen();
+ }
+
+ // Now lump together vectors
+ ArtBpath *fill1 = art_new(ArtBpath, fill1len + 1);
+ ArtBpath *fill0 = art_new(ArtBpath, fill0len + 1);
+ ArtBpath *fill1pos = fill1;
+ ArtBpath *fill0pos = fill0;
+
+ for (uint p = 0; p < _elements[e].getPathCount(); p++) {
+ if (_elements[e].getPathInfo(p).getFillStyle0() == s + 1) {
+ for (int i = 0; i < _elements[e].getPathInfo(p).getVecLen(); i++)
+ *fill0pos++ = _elements[e].getPathInfo(p).getVec()[i];
+ }
+
+ if (_elements[e].getPathInfo(p).getFillStyle1() == s + 1) {
+ for (int i = 0; i < _elements[e].getPathInfo(p).getVecLen(); i++)
+ *fill1pos++ = _elements[e].getPathInfo(p).getVec()[i];
+ }
+ }
+
+ // Close vectors
+ (*fill0pos).code = ART_END;
+ (*fill1pos).code = ART_END;
+
+ drawBez(fill1, fill0, _pixelData, width, height, _boundingBox.left, _boundingBox.top, scaleX, scaleY, -1, _elements[e].getFillStyleColor(s));
+
+ free(fill0);
+ free(fill1);
+ }
+
+ //// Draw strokes
+ for (uint s = 0; s < _elements[e].getLineStyleCount(); s++) {
+ double penWidth = _elements[e].getLineStyleWidth(s);
+ penWidth *= sqrt(fabs(scaleX * scaleY));
+
+ for (uint p = 0; p < _elements[e].getPathCount(); p++) {
+ if (_elements[e].getPathInfo(p).getLineStyle() == s + 1) {
+ drawBez(_elements[e].getPathInfo(p).getVec(), 0, _pixelData, width, height, _boundingBox.left, _boundingBox.top, scaleX, scaleY, penWidth, _elements[e].getLineStyleColor(s));
+ }
+ }
+ }
+ }
+}
+
+
+} // End of namespace Sword25
diff --git a/engines/sword25/gfx/panel.cpp b/engines/sword25/gfx/panel.cpp
new file mode 100644
index 0000000000..2de1de133a
--- /dev/null
+++ b/engines/sword25/gfx/panel.cpp
@@ -0,0 +1,111 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#include "sword25/gfx/panel.h"
+
+#include "sword25/kernel/inputpersistenceblock.h"
+#include "sword25/kernel/outputpersistenceblock.h"
+#include "sword25/gfx/graphicengine.h"
+#include "sword25/gfx/image/image.h"
+
+namespace Sword25 {
+
+#define BS_LOG_PREFIX "PANEL"
+
+Panel::Panel(RenderObjectPtr<RenderObject> parentPtr, int width, int height, uint color) :
+ RenderObject(parentPtr, RenderObject::TYPE_PANEL),
+ _color(color) {
+ _initSuccess = false;
+
+ _width = width;
+ _height = height;
+
+ if (_width < 0) {
+ BS_LOG_ERRORLN("Tried to initialise a panel with an invalid width (%d).", _width);
+ return;
+ }
+
+ if (_height < 0) {
+ BS_LOG_ERRORLN("Tried to initialise a panel with an invalid height (%d).", _height);
+ return;
+ }
+
+ _initSuccess = true;
+}
+
+Panel::Panel(InputPersistenceBlock &reader, RenderObjectPtr<RenderObject> parentPtr, uint handle) :
+ RenderObject(parentPtr, RenderObject::TYPE_PANEL, handle) {
+ _initSuccess = unpersist(reader);
+}
+
+Panel::~Panel() {
+}
+
+bool Panel::doRender() {
+ // Falls der Alphawert 0 ist, ist das Panel komplett durchsichtig und es muss nichts gezeichnet werden.
+ if (_color >> 24 == 0)
+ return true;
+
+ GraphicEngine *gfxPtr = Kernel::getInstance()->getGfx();
+ BS_ASSERT(gfxPtr);
+
+ return gfxPtr->fill(&_bbox, _color);
+}
+
+bool Panel::persist(OutputPersistenceBlock &writer) {
+ bool result = true;
+
+ result &= RenderObject::persist(writer);
+ writer.write(_color);
+
+ result &= RenderObject::persistChildren(writer);
+
+ return result;
+}
+
+bool Panel::unpersist(InputPersistenceBlock &reader) {
+ bool result = true;
+
+ result &= RenderObject::unpersist(reader);
+
+ uint color;
+ reader.read(color);
+ setColor(color);
+
+ result &= RenderObject::unpersistChildren(reader);
+
+ return reader.isGood() && result;
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/gfx/panel.h b/engines/sword25/gfx/panel.h
new file mode 100644
index 0000000000..6fe96369a6
--- /dev/null
+++ b/engines/sword25/gfx/panel.h
@@ -0,0 +1,73 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#ifndef SWORD25_PANEL_H
+#define SWORD25_PANEL_H
+
+#include "sword25/kernel/common.h"
+#include "sword25/gfx/renderobject.h"
+
+namespace Sword25 {
+
+class Panel : public RenderObject {
+ friend class RenderObject;
+
+private:
+ Panel(RenderObjectPtr<RenderObject> parentPtr, int width, int height, uint color);
+ Panel(InputPersistenceBlock &reader, RenderObjectPtr<RenderObject> parentPtr, uint handle);
+
+public:
+ virtual ~Panel();
+
+ uint getColor() const {
+ return _color;
+ }
+ void setColor(uint color) {
+ _color = color;
+ forceRefresh();
+ }
+
+ virtual bool persist(OutputPersistenceBlock &writer);
+ virtual bool unpersist(InputPersistenceBlock &reader);
+
+protected:
+ virtual bool doRender();
+
+private:
+ uint _color;
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/gfx/renderobject.cpp b/engines/sword25/gfx/renderobject.cpp
new file mode 100644
index 0000000000..77af0bee92
--- /dev/null
+++ b/engines/sword25/gfx/renderobject.cpp
@@ -0,0 +1,517 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#include "sword25/gfx/renderobject.h"
+
+#include "sword25/kernel/outputpersistenceblock.h"
+#include "sword25/kernel/inputpersistenceblock.h"
+
+#include "sword25/gfx/renderobjectregistry.h"
+#include "sword25/gfx/renderobjectmanager.h"
+#include "sword25/gfx/graphicengine.h"
+
+#include "sword25/gfx/bitmap.h"
+#include "sword25/gfx/staticbitmap.h"
+#include "sword25/gfx/dynamicbitmap.h"
+#include "sword25/gfx/animation.h"
+#include "sword25/gfx/panel.h"
+#include "sword25/gfx/text.h"
+#include "sword25/gfx/animationtemplate.h"
+
+namespace Sword25 {
+
+#define BS_LOG_PREFIX "RENDEROBJECT"
+
+RenderObject::RenderObject(RenderObjectPtr<RenderObject> parentPtr, TYPES type, uint handle) :
+ _managerPtr(0),
+ _parentPtr(parentPtr),
+ _x(0),
+ _y(0),
+ _z(0),
+ _oldX(-1),
+ _oldY(-1),
+ _oldZ(-1),
+ _width(0),
+ _height(0),
+ _visible(true),
+ _oldVisible(false),
+ _childChanged(true),
+ _type(type),
+ _initSuccess(false),
+ _refreshForced(true),
+ _handle(0) {
+
+ // Renderobject registrieren, abhängig vom Handle-Parameter entweder mit beliebigem oder vorgegebenen Handle.
+ if (handle == 0)
+ _handle = RenderObjectRegistry::instance().registerObject(this);
+ else
+ _handle = RenderObjectRegistry::instance().registerObject(this, handle);
+
+ if (_handle == 0)
+ return;
+
+ updateAbsolutePos();
+
+ // Dieses Objekt zu den Kindern der Elternobjektes hinzufügen, falls nicht Wurzel (ParentPtr ungültig) und dem
+ // selben RenderObjektManager zuweisen.
+ if (_parentPtr.isValid()) {
+ _managerPtr = _parentPtr->getManager();
+ _parentPtr->addObject(this->getHandle());
+ } else {
+ if (getType() != TYPE_ROOT) {
+ BS_LOG_ERRORLN("Tried to create a non-root render object and has no parent. All non-root render objects have to have a parent.");
+ return;
+ }
+ }
+
+ updateObjectState();
+
+ _initSuccess = true;
+}
+
+RenderObject::~RenderObject() {
+ // Objekt aus dem Elternobjekt entfernen.
+ if (_parentPtr.isValid())
+ _parentPtr->detatchChildren(this->getHandle());
+
+ deleteAllChildren();
+
+ // Objekt deregistrieren.
+ RenderObjectRegistry::instance().deregisterObject(this);
+}
+
+bool RenderObject::render() {
+ // Objektänderungen validieren
+ validateObject();
+
+ // Falls das Objekt nicht sichtbar ist, muss gar nichts gezeichnet werden
+ if (!_visible)
+ return true;
+
+ // Falls notwendig, wird die Renderreihenfolge der Kinderobjekte aktualisiert.
+ if (_childChanged) {
+ sortRenderObjects();
+ _childChanged = false;
+ }
+
+ // Objekt zeichnen.
+ doRender();
+
+ // Dann müssen die Kinder gezeichnet werden
+ RENDEROBJECT_ITER it = _children.begin();
+ for (; it != _children.end(); ++it)
+ if (!(*it)->render())
+ return false;
+
+ return true;
+}
+
+void RenderObject::validateObject() {
+ // Die Veränderungen in den Objektvariablen aufheben
+ _oldBbox = _bbox;
+ _oldVisible = _visible;
+ _oldX = _x;
+ _oldY = _y;
+ _oldZ = _z;
+ _refreshForced = false;
+}
+
+bool RenderObject::updateObjectState() {
+ // Falls sich das Objekt verändert hat, muss der interne Zustand neu berechnet werden und evtl. Update-Regions für den nächsten Frame
+ // registriert werden.
+ if ((calcBoundingBox() != _oldBbox) ||
+ (_visible != _oldVisible) ||
+ (_x != _oldX) ||
+ (_y != _oldY) ||
+ (_z != _oldZ) ||
+ _refreshForced) {
+ // Renderrang des Objektes neu bestimmen, da sich dieser verändert haben könnte
+ if (_parentPtr.isValid())
+ _parentPtr->signalChildChange();
+
+ // Die Bounding-Box neu berechnen und Update-Regions registrieren.
+ updateBoxes();
+
+ // Änderungen Validieren
+ validateObject();
+ }
+
+ // Dann muss der Objektstatus der Kinder aktualisiert werden.
+ RENDEROBJECT_ITER it = _children.begin();
+ for (; it != _children.end(); ++it)
+ if (!(*it)->updateObjectState())
+ return false;
+
+ return true;
+}
+
+void RenderObject::updateBoxes() {
+ // Bounding-Box aktualisieren
+ _bbox = calcBoundingBox();
+}
+
+Common::Rect RenderObject::calcBoundingBox() const {
+ // Die Bounding-Box mit der Objektgröße initialisieren.
+ Common::Rect bbox(0, 0, _width, _height);
+
+ // Die absolute Position der Bounding-Box berechnen.
+ bbox.translate(_absoluteX, _absoluteY);
+
+ // Die Bounding-Box am Elternobjekt clippen.
+ if (_parentPtr.isValid()) {
+ bbox.clip(_parentPtr->getBbox());
+ }
+
+ return bbox;
+}
+
+void RenderObject::calcAbsolutePos(int &x, int &y) const {
+ x = calcAbsoluteX();
+ y = calcAbsoluteY();
+}
+
+int RenderObject::calcAbsoluteX() const {
+ if (_parentPtr.isValid())
+ return _parentPtr->getAbsoluteX() + _x;
+ else
+ return _x;
+}
+
+int RenderObject::calcAbsoluteY() const {
+ if (_parentPtr.isValid())
+ return _parentPtr->getAbsoluteY() + _y;
+ else
+ return _y;
+}
+
+void RenderObject::deleteAllChildren() {
+ // Es ist nicht notwendig die Liste zu iterieren, da jedes Kind für sich DetatchChildren an diesem Objekt aufruft und sich somit
+ // selber entfernt. Daher muss immer nur ein beliebiges Element (hier das letzte) gelöscht werden, bis die Liste leer ist.
+ while (!_children.empty()) {
+ RenderObjectPtr<RenderObject> curPtr = _children.back();
+ curPtr.erase();
+ }
+}
+
+bool RenderObject::addObject(RenderObjectPtr<RenderObject> pObject) {
+ if (!pObject.isValid()) {
+ BS_LOG_ERRORLN("Tried to add a null object to a renderobject.");
+ return false;
+ }
+
+ // Objekt in die Kinderliste einfügen.
+ _children.push_back(pObject);
+
+ // Sicherstellen, dass vor dem nächsten Rendern die Renderreihenfolge aktualisiert wird.
+ if (_parentPtr.isValid())
+ _parentPtr->signalChildChange();
+
+ return true;
+}
+
+bool RenderObject::detatchChildren(RenderObjectPtr<RenderObject> pObject) {
+ // Kinderliste durchgehen und Objekt entfernen falls vorhanden
+ RENDEROBJECT_ITER it = _children.begin();
+ for (; it != _children.end(); ++it)
+ if (*it == pObject) {
+ _children.erase(it);
+ return true;
+ }
+
+ BS_LOG_ERRORLN("Tried to detach children from a render object that isn't its parent.");
+ return false;
+}
+
+void RenderObject::sortRenderObjects() {
+ Common::sort(_children.begin(), _children.end(), greater);
+}
+
+void RenderObject::updateAbsolutePos() {
+ calcAbsolutePos(_absoluteX, _absoluteY);
+
+ RENDEROBJECT_ITER it = _children.begin();
+ for (; it != _children.end(); ++it)
+ (*it)->updateAbsolutePos();
+}
+
+bool RenderObject::getObjectIntersection(RenderObjectPtr<RenderObject> pObject, Common::Rect &result) {
+ result = pObject->getBbox();
+ result.clip(_bbox);
+ return result.isValidRect();
+}
+
+void RenderObject::setPos(int x, int y) {
+ _x = x;
+ _y = y;
+ updateAbsolutePos();
+}
+
+void RenderObject::setX(int x) {
+ _x = x;
+ updateAbsolutePos();
+}
+
+void RenderObject::setY(int y) {
+ _y = y;
+ updateAbsolutePos();
+}
+
+void RenderObject::setZ(int z) {
+ if (z < 0)
+ BS_LOG_ERRORLN("Tried to set a negative Z value (%d).", z);
+ else
+ _z = z;
+}
+
+void RenderObject::setVisible(bool visible) {
+ _visible = visible;
+}
+
+RenderObjectPtr<Animation> RenderObject::addAnimation(const Common::String &filename) {
+ RenderObjectPtr<Animation> aniPtr((new Animation(this->getHandle(), filename))->getHandle());
+ if (aniPtr.isValid() && aniPtr->getInitSuccess())
+ return aniPtr;
+ else {
+ if (aniPtr.isValid())
+ aniPtr.erase();
+ return RenderObjectPtr<Animation>();
+ }
+}
+
+RenderObjectPtr<Animation> RenderObject::addAnimation(const AnimationTemplate &animationTemplate) {
+ Animation *aniPtr = new Animation(this->getHandle(), animationTemplate);
+ if (aniPtr && aniPtr->getInitSuccess())
+ return aniPtr->getHandle();
+ else {
+ delete aniPtr;
+ return RenderObjectPtr<Animation>();
+ }
+}
+
+RenderObjectPtr<Bitmap> RenderObject::addBitmap(const Common::String &filename) {
+ RenderObjectPtr<Bitmap> bitmapPtr((new StaticBitmap(this->getHandle(), filename))->getHandle());
+ if (bitmapPtr.isValid() && bitmapPtr->getInitSuccess())
+ return RenderObjectPtr<Bitmap>(bitmapPtr);
+ else {
+ if (bitmapPtr.isValid())
+ bitmapPtr.erase();
+ return RenderObjectPtr<Bitmap>();
+ }
+}
+
+RenderObjectPtr<Bitmap> RenderObject::addDynamicBitmap(uint width, uint height) {
+ RenderObjectPtr<Bitmap> bitmapPtr((new DynamicBitmap(this->getHandle(), width, height))->getHandle());
+ if (bitmapPtr.isValid() && bitmapPtr->getInitSuccess())
+ return bitmapPtr;
+ else {
+ if (bitmapPtr.isValid())
+ bitmapPtr.erase();
+ return RenderObjectPtr<Bitmap>();
+ }
+}
+
+RenderObjectPtr<Panel> RenderObject::addPanel(int width, int height, uint color) {
+ RenderObjectPtr<Panel> panelPtr((new Panel(this->getHandle(), width, height, color))->getHandle());
+ if (panelPtr.isValid() && panelPtr->getInitSuccess())
+ return panelPtr;
+ else {
+ if (panelPtr.isValid())
+ panelPtr.erase();
+ return RenderObjectPtr<Panel>();
+ }
+}
+
+RenderObjectPtr<Text> RenderObject::addText(const Common::String &font, const Common::String &text) {
+ RenderObjectPtr<Text> textPtr((new Text(this->getHandle()))->getHandle());
+ if (textPtr.isValid() && textPtr->getInitSuccess() && textPtr->setFont(font)) {
+ textPtr->setText(text);
+ return textPtr;
+ } else {
+ if (textPtr.isValid())
+ textPtr.erase();
+ return RenderObjectPtr<Text>();
+ }
+}
+
+bool RenderObject::persist(OutputPersistenceBlock &writer) {
+ // Typ und Handle werden als erstes gespeichert, damit beim Laden ein Objekt vom richtigen Typ mit dem richtigen Handle erzeugt werden kann.
+ writer.write(static_cast<uint>(_type));
+ writer.write(_handle);
+
+ // Restliche Objekteigenschaften speichern.
+ writer.write(_x);
+ writer.write(_y);
+ writer.write(_absoluteX);
+ writer.write(_absoluteY);
+ writer.write(_z);
+ writer.write(_width);
+ writer.write(_height);
+ writer.write(_visible);
+ writer.write(_childChanged);
+ writer.write(_initSuccess);
+ writer.write(_bbox.left);
+ writer.write(_bbox.top);
+ writer.write(_bbox.right);
+ writer.write(_bbox.bottom);
+ writer.write(_oldBbox.left);
+ writer.write(_oldBbox.top);
+ writer.write(_oldBbox.right);
+ writer.write(_oldBbox.bottom);
+ writer.write(_oldX);
+ writer.write(_oldY);
+ writer.write(_oldZ);
+ writer.write(_oldVisible);
+ writer.write(_parentPtr.isValid() ? _parentPtr->getHandle() : 0);
+ writer.write(_refreshForced);
+
+ return true;
+}
+
+bool RenderObject::unpersist(InputPersistenceBlock &reader) {
+ // Typ und Handle wurden schon von RecreatePersistedRenderObject() ausgelesen. Jetzt werden die restlichen Objekteigenschaften ausgelesen.
+ reader.read(_x);
+ reader.read(_y);
+ reader.read(_absoluteX);
+ reader.read(_absoluteY);
+ reader.read(_z);
+ reader.read(_width);
+ reader.read(_height);
+ reader.read(_visible);
+ reader.read(_childChanged);
+ reader.read(_initSuccess);
+ reader.read(_bbox.left);
+ reader.read(_bbox.top);
+ reader.read(_bbox.right);
+ reader.read(_bbox.bottom);
+ reader.read(_oldBbox.left);
+ reader.read(_oldBbox.top);
+ reader.read(_oldBbox.right);
+ reader.read(_oldBbox.bottom);
+ reader.read(_oldX);
+ reader.read(_oldY);
+ reader.read(_oldZ);
+ reader.read(_oldVisible);
+ uint parentHandle;
+ reader.read(parentHandle);
+ _parentPtr = RenderObjectPtr<RenderObject>(parentHandle);
+ reader.read(_refreshForced);
+
+ updateAbsolutePos();
+ updateObjectState();
+
+ return reader.isGood();
+}
+
+bool RenderObject::persistChildren(OutputPersistenceBlock &writer) {
+ bool result = true;
+
+ // Kinderanzahl speichern.
+ writer.write(_children.size());
+
+ // Rekursiv alle Kinder speichern.
+ RENDEROBJECT_LIST::iterator it = _children.begin();
+ while (it != _children.end()) {
+ result &= (*it)->persist(writer);
+ ++it;
+ }
+
+ return result;
+}
+
+bool RenderObject::unpersistChildren(InputPersistenceBlock &reader) {
+ bool result = true;
+
+ // Kinderanzahl einlesen.
+ uint childrenCount;
+ reader.read(childrenCount);
+ if (!reader.isGood())
+ return false;
+
+ // Alle Kinder rekursiv wieder herstellen.
+ for (uint i = 0; i < childrenCount; ++i) {
+ if (!recreatePersistedRenderObject(reader).isValid())
+ return false;
+ }
+
+ return result && reader.isGood();
+}
+
+RenderObjectPtr<RenderObject> RenderObject::recreatePersistedRenderObject(InputPersistenceBlock &reader) {
+ RenderObjectPtr<RenderObject> result;
+
+ // Typ und Handle auslesen.
+ uint type;
+ uint handle;
+ reader.read(type);
+ reader.read(handle);
+ if (!reader.isGood())
+ return result;
+
+ switch (type) {
+ case TYPE_PANEL:
+ result = (new Panel(reader, this->getHandle(), handle))->getHandle();
+ break;
+
+ case TYPE_STATICBITMAP:
+ result = (new StaticBitmap(reader, this->getHandle(), handle))->getHandle();
+ break;
+
+ case TYPE_DYNAMICBITMAP:
+ result = (new DynamicBitmap(reader, this->getHandle(), handle))->getHandle();
+ break;
+
+ case TYPE_TEXT:
+ result = (new Text(reader, this->getHandle(), handle))->getHandle();
+ break;
+
+ case TYPE_ANIMATION:
+ result = (new Animation(reader, this->getHandle(), handle))->getHandle();
+ break;
+
+ default:
+ BS_LOG_ERRORLN("Cannot recreate render object of unknown type %d.", type);
+ }
+
+ return result;
+}
+
+bool RenderObject::greater(const RenderObjectPtr<RenderObject> lhs, const RenderObjectPtr<RenderObject> rhs) {
+ // Das Objekt mit dem kleinem Z-Wert müssen zuerst gerendert werden.
+ if (lhs->_z != rhs->_z)
+ return lhs->_z < rhs->_z;
+ // Falls der Z-Wert gleich ist, wird das weiter oben gelegenen Objekte zuerst gezeichnet.
+ return lhs->_y < rhs->_y;
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/gfx/renderobject.h b/engines/sword25/gfx/renderobject.h
new file mode 100644
index 0000000000..7b7b9047f7
--- /dev/null
+++ b/engines/sword25/gfx/renderobject.h
@@ -0,0 +1,525 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+/*
+ BS_RenderObject
+ ---------------
+ Dieses ist die Klasse die sämtliche sichtbaren Objekte beschreibt. Alle anderen sichtbaren Objekte müssen von ihr abgeleitet werden.
+ Diese Klasse erledigt Aufgaben wie: minimales Neuzeichnen, Renderreihenfolge, Objekthierachie.
+ Alle BS_RenderObject Instanzen werden von einem BS_RenderObjectManager in einem Baum verwaltet.
+
+ Autor: Malte Thiesen
+*/
+
+#ifndef SWORD25_RENDEROBJECT_H
+#define SWORD25_RENDEROBJECT_H
+
+#include "sword25/kernel/common.h"
+#include "sword25/kernel/persistable.h"
+#include "common/rect.h"
+#include "sword25/gfx/renderobjectptr.h"
+
+#include "common/list.h"
+
+namespace Sword25 {
+
+class Kernel;
+class RenderObjectManager;
+class Bitmap;
+class Animation;
+class AnimationTemplate;
+class Panel;
+class Text;
+
+// Klassendefinition
+/**
+ @brief Dieses ist die Klasse die sämtliche sichtbaren Objekte beschreibt.
+
+ Alle anderen sichtbaren Objekte müssen von ihr abgeleitet werden.
+ Diese Klasse erledigt Aufgaben wie: minimales Neuzeichnen, Renderreihenfolge, Objekthierachie.
+ Alle BS_RenderObject Instanzen werden von einem BS_RenderObjektManager in einem Baum verwaltet.
+ */
+class RenderObject {
+public:
+ // Konstanten
+ // ----------
+ enum TYPES {
+ /// Das Wurzelobjekt. Siehe BS_RenderObjectManager
+ TYPE_ROOT,
+ /// Ein Image. Siehe BS_Bitmap.
+ TYPE_STATICBITMAP,
+ TYPE_DYNAMICBITMAP,
+ /// Eine Animation. Siehe BS_Animation.
+ TYPE_ANIMATION,
+ /// Eine farbige Fläche. Siehe BS_Panel.
+ TYPE_PANEL,
+ /// Ein Text. Siehe BS_Text.
+ TYPE_TEXT,
+ /// Ein unbekannter Objekttyp. Diesen Typ sollte kein Renderobjekt annehmen.
+ TYPE_UNKNOWN
+ };
+
+ // Add-Methoden
+ // ------------
+
+ /**
+ @brief Erzeugt ein Bitmap als Kinderobjekt des Renderobjektes.
+ @param FileName der Dateiname der Quellbilddatei
+ @return Gibt einen BS_RenderObjectPtr auf das erzeugte Objekt zurück.<br>
+ Falls ein Fehler aufgetreten ist wird ein ungültiger BS_RenderObjectPtr zurückgegeben.
+ */
+ RenderObjectPtr<Bitmap> addBitmap(const Common::String &fileName);
+ /**
+ @brief Erzeugt ein veränderbares Bitmap als Kinderobjekt des Renderobjektes.
+ @param Width die Breite des Bitmaps
+ @param Height die Höhe des Bitmaps
+ @return Gibt einen BS_RenderObjectPtr auf das erzeugte Objekt zurück.<br>
+ Falls ein Fehler aufgetreten ist wird ein ungültiger BS_RenderObjectPtr zurückgegeben.
+ */
+ RenderObjectPtr<Bitmap> addDynamicBitmap(uint width, uint height);
+ /**
+ @brief Erzeugt eine Animation auf Basis einer Animationsdatei als Kinderobjekt des Renderobjektes.
+ @param FileName der Dateiname der Quelldatei
+ @return Gibt einen BS_RenderObjectPtr auf das erzeugte Objekt zurück.<br>
+ Falls ein Fehler aufgetreten ist wird ein ungültiger BS_RenderObjectPtr zurückgegeben.
+ */
+ RenderObjectPtr<Animation> addAnimation(const Common::String &fileName);
+ /**
+ @brief Erzeugt eine Animation auf Basis eines Animationstemplate als Kinderobjekt des Renderobjektes.
+ @param pAnimationTemplate ein Pointer auf das Animationstemplate
+ @return Gibt einen BS_RenderObjectPtr auf das erzeugte Objekt zurück.<br>
+ Falls ein Fehler aufgetreten ist wird ein ungültiger BS_RenderObjectPtr zurückgegeben.
+ @remark Das Renderobjekt übernimmt die Verwaltung des Animationstemplate.
+ */
+ RenderObjectPtr<Animation> addAnimation(const AnimationTemplate &animationTemplate);
+ /**
+ @brief Erzeugt ein neues Farbpanel als Kinderobjekt des Renderobjektes.
+ @param Width die Breite des Panels
+ @param Height die Höhe des Panels
+ @param Color die Farbe des Panels.<br>
+ Der Standardwert ist Schwarz (BS_RGB(0, 0, 0)).
+ @return Gibt einen BS_RenderObjectPtr auf das erzeugte Objekt zurück.<br>
+ Falls ein Fehler aufgetreten ist wird ein ungültiger BS_RenderObjectPtr zurückgegeben.
+ */
+
+ RenderObjectPtr<Panel> addPanel(int width, int height, uint color = 0xff000000);
+ /**
+ @brief Erzeugt ein Textobjekt als Kinderobjekt des Renderobjektes.
+ @param Font der Dateiname des zu verwendenen Fonts
+ @param Text der anzuzeigende Text.<br>
+ Der Standardwert ist "".
+ @return Gibt einen BS_RenderObjectPtr auf das erzeugte Objekt zurück.<br>
+ Falls ein Fehler aufgetreten ist wird ein ungültiger BS_RenderObjectPtr zurückgegeben.
+ */
+ RenderObjectPtr<Text> addText(const Common::String &font, const Common::String &text = "");
+
+ // Cast-Methoden
+ // -------------
+ /**
+ @brief Castet das Objekt zu einem BS_Bitmap-Objekt wenn zulässig.
+ @return Gibt einen BS_RenderObjectPtr auf das Objekt zurück.<br>
+ Falls der Cast nicht zulässig ist, wird ein ungültiger BS_RenderObjectPtr zurückgegeben.
+ */
+ RenderObjectPtr<Bitmap> toBitmap() {
+ if (_type == TYPE_STATICBITMAP || _type == TYPE_DYNAMICBITMAP)
+ return RenderObjectPtr<Bitmap>(this->getHandle());
+ else
+ return RenderObjectPtr<Bitmap>();
+ }
+ /**
+ @brief Castet das Objekt zu einem BS_Animation-Objekt wenn zulässig.
+ @return Gibt einen BS_RenderObjectPtr auf das Objekt zurück.<br>
+ Falls der Cast nicht zulässig ist, wird ein ungültiger BS_RenderObjectPtr zurückgegeben.
+ */
+ RenderObjectPtr<Animation> toAnimation() {
+ if (_type == TYPE_ANIMATION)
+ return RenderObjectPtr<Animation>(this->getHandle());
+ else
+ return RenderObjectPtr<Animation>();
+ }
+ /**
+ @brief Castet das Objekt zu einem BS_Panel-Objekt wenn zulässig.
+ @return Gibt einen BS_RenderObjectPtr auf das Objekt zurück.<br>
+ Falls der Cast nicht zulässig ist, wird ein ungültiger BS_RenderObjectPtr zurückgegeben.
+ */
+ RenderObjectPtr<Panel> toPanel() {
+ if (_type == TYPE_PANEL)
+ return RenderObjectPtr<Panel>(this->getHandle());
+ else
+ return RenderObjectPtr<Panel>();
+ }
+ /**
+ @brief Castet das Object zu einem BS_Text-Objekt wenn zulässig.
+ @return Gibt einen BS_RenderObjectPtr auf das Objekt zurück.<br>
+ Falls der Cast nicht zulässig ist, wird ein ungültiger BS_RenderObjectPtr zurückgegeben.
+ */
+ RenderObjectPtr<Text> toText() {
+ if (_type == TYPE_TEXT)
+ return RenderObjectPtr<Text>(this->getHandle());
+ else
+ return RenderObjectPtr<Text>();
+ }
+
+ // Konstruktor / Desktruktor
+ // -------------------------
+ /**
+ @brief Erzeugt ein neues BS_RenderObject.
+ @param pKernel ein Pointer auf den Kernel
+ @param pParent ein Pointer auf das Elternobjekt des neuen Objektes im Objektbaum.<br>
+ Der Pointer darf nicht NULL sein.
+ @param Type der Objekttyp<br>
+ Der Typ BS_RenderObject::TYPE_ROOT ist nicht zulässig. Wurzelknoten müssen mit dem alternativen Konstruktor erzeugt
+ werden.
+ @param Handle das Handle, welches dem Objekt zugewiesen werden soll.<br>
+ Dieser Parameter erzwingt ein bestimmtes Handle für das neue Objekt, oder wählt automatisch ein Handle, wenn der Parameter 0 ist.
+ Ist das gewünschte Handle bereits vergeben, gibt GetInitSuccess() false zurück.<br>
+ Der Standardwert ist 0.
+ @remark Nach dem Aufruf des Konstruktors kann über die Methode GetInitSuccess() abgefragt werden, ob die Konstruktion erfolgreich war.<br>
+ Es ist nicht notwendig alle BS_RenderObject Instanzen einzeln zu löschen. Dieses geschiet automatisch beim Löschen eines
+ Vorfahren oder beim Löschen des zuständigen BS_RenderObjectManager.
+ */
+ RenderObject(RenderObjectPtr<RenderObject> pParent, TYPES type, uint handle = 0);
+ virtual ~RenderObject();
+
+ // Interface
+ // ---------
+ /**
+ @brief Rendert des Objekt und alle seine Unterobjekte.
+ @return Gibt false zurück, falls beim Rendern ein Fehler aufgetreten ist.
+ @remark Vor jedem Aufruf dieser Methode muss ein Aufruf von UpdateObjectState() erfolgt sein.
+ Dieses kann entweder direkt geschehen oder durch den Aufruf von UpdateObjectState() an einem Vorfahren-Objekt.<br>
+ Diese Methode darf nur von BS_RenderObjectManager aufgerufen werden.
+ */
+ bool render();
+ /**
+ @brief Bereitet das Objekt und alle seine Unterobjekte auf einen Rendervorgang vor.
+ Hierbei werden alle Dirty-Rectangles berechnet und die Renderreihenfolge aktualisiert.
+ @return Gibt false zurück, falls ein Fehler aufgetreten ist.
+ @remark Diese Methode darf nur von BS_RenderObjectManager aufgerufen werden.
+ */
+ bool updateObjectState();
+ /**
+ @brief Löscht alle Kinderobjekte.
+ */
+ void deleteAllChildren();
+
+ // Accessor-Methoden
+ // -----------------
+ /**
+ @brief Setzt die Position des Objektes.
+ @param X die neue X-Koordinate des Objektes relativ zum Elternobjekt.
+ @param Y die neue Y-Koordinate des Objektes relativ zum Elternobjekt.
+ */
+ virtual void setPos(int x, int y);
+ /**
+ @brief Setzt die Position des Objektes auf der X-Achse.
+ @param X die neue X-Koordinate des Objektes relativ zum Elternobjekt.
+ */
+ virtual void setX(int x);
+ /**
+ @brief Setzt die Position des Objektes auf der Y-Achse.
+ @param Y die neue Y-Koordinate des Objektes relativ zum Elternobjekt.
+ */
+ virtual void setY(int y);
+ /**
+ @brief Setzt den Z-Wert des Objektes.
+ @param Z der neue Z-Wert des Objektes relativ zum Elternobjekt<br>
+ Negative Z-Werte sind nicht zulässig.
+ @remark Der Z-Wert legt die Renderreihenfolge der Objekte fest. Objekte mit niedrigem Z-Wert werden vor Objekten mit höherem
+ Z-Wert gezeichnet. Je höher der Z-Wert desto weiter "vorne" liegt ein Objekt also.<br>
+ Wie alle andere Attribute ist auch dieses relativ zum Elternobjekt, ein Kinderobjekt kann also nie unter seinem
+ Elternobjekt liegen, auch wenn es einen Z-Wert von 0 hat.
+ */
+ virtual void setZ(int z);
+ /**
+ @brief Setzt die Sichtbarkeit eine Objektes.
+ @param Visible der neue Sichtbarkeits-Zustand des Objektes<br>
+ true entspricht sichtbar, false entspricht unsichtbar.
+ */
+ virtual void setVisible(bool visible);
+ /**
+ @brief Gibt die Position des Objektes auf der X-Achse relativ zum Elternobjekt zurück.
+ */
+ virtual int getX() const {
+ return _x;
+ }
+ /**
+ @brief Gibt die Position des Objektes auf der Y-Achse relativ zum Elternobjekt zurück.
+ */
+ virtual int getY() const {
+ return _y;
+ }
+ /**
+ @brief Gibt die absolute Position des Objektes auf der X-Achse zurück.
+ */
+ virtual int getAbsoluteX() const {
+ return _absoluteX;
+ }
+ /**
+ @brief Gibt die absolute Position des Objektes auf der Y-Achse zurück.
+ */
+ virtual int getAbsoluteY() const {
+ return _absoluteY;
+ }
+ /**
+ @brief Gibt den Z-Wert des Objektes relativ zum Elternobjekt zurück.
+ @remark Der Z-Wert legt die Renderreihenfolge der Objekte fest. Objekte mit niedrigem Z-Wert werden vor Objekten mit höherem
+ Z-Wert gezeichnet. Je höher der Z-Wert desto weiter "vorne" liegt ein Objekt also.<br>
+ Wie alle andere Attribute ist auch dieses relativ zum Elternobjekt, ein Kinderobjekt kann also nie unter seinem
+ Elternobjekt liegen, auch wenn es einen Z-Wert von 0 hat.
+ */
+ int getZ() const {
+ return _z;
+ }
+ /**
+ @brief Gibt die Breite des Objektes zurück.
+ */
+ int getWidth() const {
+ return _width;
+ }
+ /**
+ @brief Gibt die Höhe des Objektes zurück.
+ */
+ int getHeight() const {
+ return _height;
+ }
+ /**
+ @brief Gibt den Sichtbarkeitszustand des Objektes zurück.
+ @return Gibt den Sichtbarkeitszustand des Objektes zurück.<br>
+ true entspricht sichtbar, false entspricht unsichtbar.
+ */
+ bool isVisible() const {
+ return _visible;
+ }
+ /**
+ @brief Gibt den Typ des Objektes zurück.
+ */
+ TYPES getType() const {
+ return _type;
+ }
+ /**
+ @brief Gibt zurück, ob das Objekt erfolgreich initialisiert wurde.
+ @remark Hässlicher Workaround um das Problem, dass Konstruktoren keine Rückgabewerte haben.
+ */
+ bool getInitSuccess() const {
+ return _initSuccess;
+ }
+ /**
+ @brief Gibt die Bounding-Box des Objektes zurück.
+ @remark Diese Angabe erfolgt ausnahmsweise in Bildschirmkoordianten und nicht relativ zum Elternobjekt.
+ */
+ const Common::Rect &getBbox() const {
+ return _bbox;
+ }
+ /**
+ @brief Stellt sicher, dass das Objekt im nächsten Frame neu gezeichnet wird.
+ */
+ void forceRefresh() {
+ _refreshForced = true;
+ }
+ /**
+ @brief Gibt das Handle des Objekte zurück.
+ */
+ uint getHandle() const {
+ return _handle;
+ }
+
+ // Persistenz-Methoden
+ // -------------------
+ virtual bool persist(OutputPersistenceBlock &writer);
+ virtual bool unpersist(InputPersistenceBlock &reader);
+ // TODO: Evtl. protected
+ bool persistChildren(OutputPersistenceBlock &writer);
+ bool unpersistChildren(InputPersistenceBlock &reader);
+ // TODO: Evtl. private
+ RenderObjectPtr<RenderObject> recreatePersistedRenderObject(InputPersistenceBlock &reader);
+
+protected:
+ // Typen
+ // -----
+ typedef Common::List<RenderObjectPtr<RenderObject> > RENDEROBJECT_LIST;
+ typedef Common::List<RenderObjectPtr<RenderObject> >::iterator RENDEROBJECT_ITER;
+
+ int _x; ///< Die X-Position des Objektes relativ zum Eltern-Objekt
+ int _y; ///< Die Y-Position des Objektes relativ zum Eltern-Objekt
+ int _absoluteX; ///< Die absolute X-Position des Objektes
+ int _absoluteY; ///< Die absolute Y-Position des Objektes
+ int _z; ///< Der Z-Wert des Objektes relativ zum Eltern-Objekt
+ int _width; ///< Die Breite des Objektes
+ int _height; ///< Die Höhe des Objektes
+ bool _visible; ///< Ist true, wenn das Objekt sichtbar ist
+ bool _childChanged; ///< Ist true, wenn sich ein Kinderobjekt verändert hat
+ TYPES _type; ///< Der Objekttyp
+ bool _initSuccess; ///< Ist true, wenn Objekt erfolgreich intialisiert werden konnte
+ Common::Rect _bbox; ///< Die Bounding-Box des Objektes in Bildschirmkoordinaten
+
+ // Kopien der Variablen, die für die Errechnung des Dirty-Rects und zur Bestimmung der Objektveränderung notwendig sind
+ Common::Rect _oldBbox;
+ int _oldX;
+ int _oldY;
+ int _oldZ;
+ bool _oldVisible;
+
+ /// Ein Pointer auf den BS_RenderObjektManager, der das Objekt verwaltet.
+ RenderObjectManager *_managerPtr;
+
+ // Render-Methode
+ // --------------
+ /**
+ @brief Einschubmethode, die den tatsächlichen Redervorgang durchführt.
+
+ Diese Methode wird von Render() aufgerufen um das Objekt darzustellen.
+ Diese Methode sollte von allen Klassen implementiert werden, die von BS_RederObject erben, um das Zeichnen umzusetzen.
+
+ @return Gibt false zurück, falls das Rendern fehlgeschlagen ist.
+ @remark
+ */
+ virtual bool doRender() = 0; // { return true; };
+
+ // RenderObject-Baum Variablen
+ // ---------------------------
+ // Der Baum legt die hierachische Ordnung der BS_RenderObjects fest.
+ // Alle Eigenschaften wie X, Y, Z und Visible eines BS_RenderObjects sind relativ zu denen seines Vaters.
+ // Außerdem sind die Objekte von links nach rechts in ihrer Renderreihenfolge sortiert.
+ // Das primäre Sortierkriterium ist hierbei der Z-Wert und das sekundäre der Y-Wert (von oben nach unten).
+ // Beispiel:
+ // Screen
+ // / | \.
+ // / | \.
+ // / | \.
+ // / | \.
+ // Background Interface Maus
+ // / \ / | \.
+ // / \ / | \.
+ // George Tür Icn1 Icn2 Icn3
+ //
+ // Wenn jetzt das Interface mit SetVisible() ausgeblendet würde, verschwinden auch die Icons, die sich im Interface
+ // befinden.
+ // Wenn der Hintergrund bewegt wird (Scrolling), bewegen sich auch die darauf befindlichen Gegenstände und Personen.
+
+ /// Ein Pointer auf das Elternobjekt.
+ RenderObjectPtr<RenderObject> _parentPtr;
+ /// Die Liste der Kinderobjekte nach der Renderreihenfolge geordnet
+ RENDEROBJECT_LIST _children;
+
+ /**
+ @brief Gibt einen Pointer auf den BS_RenderObjektManager zurück, der das Objekt verwaltet.
+ */
+ RenderObjectManager *getManager() const {
+ return _managerPtr;
+ }
+ /**
+ @brief Fügt dem Objekt ein neues Kinderobjekt hinzu.
+ @param pObject ein Pointer auf das einzufügende Objekt
+ @return Gibt false zurück, falls das Objekt nicht eingefügt werden konnte.
+ */
+ bool addObject(RenderObjectPtr<RenderObject> pObject);
+
+private:
+ /// Ist true, wenn das Objekt in nächsten Frame neu gezeichnet werden soll
+ bool _refreshForced;
+
+ uint _handle;
+
+ /**
+ @brief Entfernt ein Objekt aus der Kinderliste.
+ @param pObject ein Pointer auf das zu entfernende Objekt
+ @return Gibt false zurück, falls das zu entfernende Objekt nicht in der Liste gefunden werden konnte.
+ */
+ bool detatchChildren(RenderObjectPtr<RenderObject> pObject);
+ /**
+ @brief Berechnet die Bounding-Box und registriert das Dirty-Rect beim BS_RenderObjectManager.
+ */
+ void updateBoxes();
+ /**
+ @brief Berechnet die Bounding-Box des Objektes.
+ @return Gibt die Bounding-Box des Objektes in Bildschirmkoordinaten zurück.
+ */
+ Common::Rect calcBoundingBox() const;
+ /**
+ @brief Berechnet das Dirty-Rectangle des Objektes.
+ @return Gibt das Dirty-Rectangle des Objektes in Bildschirmkoordinaten zurück.
+ */
+ Common::Rect calcDirtyRect() const;
+ /**
+ @brief Berechnet die absolute Position des Objektes.
+ */
+ void calcAbsolutePos(int &x, int &y) const;
+ /**
+ @brief Berechnet die absolute Position des Objektes auf der X-Achse.
+ */
+ int calcAbsoluteX() const;
+ /**
+ @brief Berechnet die absolute Position des Objektes.
+ */
+ int calcAbsoluteY() const;
+ /**
+ @brief Sortiert alle Kinderobjekte nach ihrem Renderang.
+ */
+ void sortRenderObjects();
+ /**
+ @brief Validiert den Zustand eines Objektes nachdem die durch die Veränderung verursachten Folgen abgearbeitet wurden.
+ */
+ void validateObject();
+ /**
+ @brief Berechnet die absolute Position des Objektes und aller seiner Kinderobjekte neu.
+
+ Diese Methode muss aufgerufen werden, wann immer sich die Position des Objektes verändert. Damit die Kinderobjekte immer die
+ richtige absolute Position haben.
+ */
+ void updateAbsolutePos();
+ /**
+ @brief Teilt dem Objekt mit, dass sich eines seiner Kinderobjekte dahingehend verändert hat, die eine erneute Bestimmung der
+ Rendereihenfolge verlangt.
+ */
+ void signalChildChange() {
+ _childChanged = true;
+ }
+ /**
+ @brief Berechnet des Schnittrechteck der Bounding-Box des Objektes mit einem anderen Objekt.
+ @param pObjekt ein Pointer auf das Objekt mit dem geschnitten werden soll
+ @param Result das Ergebnisrechteck
+ @return Gibt false zurück, falls sich die Objekte gar nicht schneiden.
+ */
+ bool getObjectIntersection(RenderObjectPtr<RenderObject> pObject, Common::Rect &result);
+ /**
+ @brief Vergleichsoperator der auf Objektpointern basiert statt auf Objekten.
+ @remark Diese Methode wird fürs Sortieren der Kinderliste nach der Rendereihenfolge benutzt.
+ */
+ static bool greater(const RenderObjectPtr<RenderObject> lhs, const RenderObjectPtr<RenderObject> rhs);
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/gfx/renderobjectmanager.cpp b/engines/sword25/gfx/renderobjectmanager.cpp
new file mode 100644
index 0000000000..94f7a04b45
--- /dev/null
+++ b/engines/sword25/gfx/renderobjectmanager.cpp
@@ -0,0 +1,151 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#include "sword25/gfx/renderobjectmanager.h"
+
+#include "sword25/kernel/kernel.h"
+#include "sword25/kernel/inputpersistenceblock.h"
+#include "sword25/kernel/outputpersistenceblock.h"
+#include "sword25/gfx/graphicengine.h"
+#include "sword25/gfx/animationtemplateregistry.h"
+#include "common/rect.h"
+#include "sword25/gfx/renderobject.h"
+#include "sword25/gfx/timedrenderobject.h"
+#include "sword25/gfx/rootrenderobject.h"
+
+namespace Sword25 {
+
+#define BS_LOG_PREFIX "RENDEROBJECTMANAGER"
+
+RenderObjectManager::RenderObjectManager(int width, int height, int framebufferCount) :
+ _frameStarted(false) {
+ // Wurzel des BS_RenderObject-Baumes erzeugen.
+ _rootPtr = (new RootRenderObject(this, width, height))->getHandle();
+}
+
+RenderObjectManager::~RenderObjectManager() {
+ // Die Wurzel des Baumes löschen, damit werden alle BS_RenderObjects mitgelöscht.
+ _rootPtr.erase();
+}
+
+void RenderObjectManager::startFrame() {
+ _frameStarted = true;
+
+ // Verstrichene Zeit bestimmen
+ int timeElapsed = Kernel::getInstance()->getGfx()->getLastFrameDurationMicro();
+
+ // Alle BS_TimedRenderObject Objekte über den Framestart und die verstrichene Zeit in Kenntnis setzen
+ RenderObjectList::iterator iter = _timedRenderObjects.begin();
+ for (; iter != _timedRenderObjects.end(); ++iter)
+ (*iter)->frameNotification(timeElapsed);
+}
+
+bool RenderObjectManager::render() {
+ // Den Objekt-Status des Wurzelobjektes aktualisieren. Dadurch werden rekursiv alle Baumelemente aktualisiert.
+ // Beim aktualisieren des Objekt-Status werden auch die Update-Rects gefunden, so dass feststeht, was neu gezeichnet
+ // werden muss.
+ if (!_rootPtr.isValid() || !_rootPtr->updateObjectState())
+ return false;
+
+ _frameStarted = false;
+
+ // Die Render-Methode der Wurzel aufrufen. Dadurch wird das rekursive Rendern der Baumelemente angestoßen.
+ return _rootPtr->render();
+}
+
+void RenderObjectManager::attatchTimedRenderObject(RenderObjectPtr<TimedRenderObject> renderObjectPtr) {
+ _timedRenderObjects.push_back(renderObjectPtr);
+}
+
+void RenderObjectManager::detatchTimedRenderObject(RenderObjectPtr<TimedRenderObject> renderObjectPtr) {
+ for (uint i = 0; i < _timedRenderObjects.size(); i++)
+ if (_timedRenderObjects[i] == renderObjectPtr) {
+ _timedRenderObjects.remove_at(i);
+ break;
+ }
+}
+
+bool RenderObjectManager::persist(OutputPersistenceBlock &writer) {
+ bool result = true;
+
+ // Alle Kinder des Wurzelknotens speichern. Dadurch werden alle BS_RenderObjects gespeichert rekursiv gespeichert.
+ result &= _rootPtr->persistChildren(writer);
+
+ writer.write(_frameStarted);
+
+ // Referenzen auf die TimedRenderObjects persistieren.
+ writer.write(_timedRenderObjects.size());
+ RenderObjectList::const_iterator iter = _timedRenderObjects.begin();
+ while (iter != _timedRenderObjects.end()) {
+ writer.write((*iter)->getHandle());
+ ++iter;
+ }
+
+ // Alle BS_AnimationTemplates persistieren.
+ result &= AnimationTemplateRegistry::instance().persist(writer);
+
+ return result;
+}
+
+bool RenderObjectManager::unpersist(InputPersistenceBlock &reader) {
+ bool result = true;
+
+ // Alle Kinder des Wurzelknotens löschen. Damit werden alle BS_RenderObjects gelöscht.
+ _rootPtr->deleteAllChildren();
+
+ // Alle BS_RenderObjects wieder hestellen.
+ if (!_rootPtr->unpersistChildren(reader))
+ return false;
+
+ reader.read(_frameStarted);
+
+ // Momentan gespeicherte Referenzen auf TimedRenderObjects löschen.
+ _timedRenderObjects.resize(0);
+
+ // Referenzen auf die TimedRenderObjects wieder herstellen.
+ uint timedObjectCount;
+ reader.read(timedObjectCount);
+ for (uint i = 0; i < timedObjectCount; ++i) {
+ uint handle;
+ reader.read(handle);
+ _timedRenderObjects.push_back(handle);
+ }
+
+ // Alle BS_AnimationTemplates wieder herstellen.
+ result &= AnimationTemplateRegistry::instance().unpersist(reader);
+
+ return result;
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/gfx/renderobjectmanager.h b/engines/sword25/gfx/renderobjectmanager.h
new file mode 100644
index 0000000000..8511382d6e
--- /dev/null
+++ b/engines/sword25/gfx/renderobjectmanager.h
@@ -0,0 +1,129 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+/*
+ BS_RenderObjectManager
+ ----------------------
+ Diese Klasse ist für die Verwaltung von BS_RenderObjects zuständig.
+
+ Sie sorgt z.B. dafür, dass die BS_RenderObjects in der richtigen Reihenfolge gerendert werden.
+
+ Autor: Malte Thiesen
+*/
+
+#ifndef SWORD25_RENDEROBJECTMANAGER_H
+#define SWORD25_RENDEROBJECTMANAGER_H
+
+#include "common/rect.h"
+#include "sword25/kernel/common.h"
+#include "sword25/gfx/renderobjectptr.h"
+#include "sword25/kernel/persistable.h"
+
+namespace Sword25 {
+
+class Kernel;
+class RenderObject;
+class TimedRenderObject;
+
+/**
+ @brief Diese Klasse ist für die Verwaltung von BS_RenderObjects zuständig.
+
+ Sie sorgt dafür, dass die BS_RenderObjects in der richtigen Reihenfolge gerendert werden und ermöglicht den Zugriff auf die
+ BS_RenderObjects über einen String.
+*/
+class RenderObjectManager : public Persistable {
+public:
+ /**
+ @brief Erzeugt ein neues BS_RenderObjectManager-Objekt.
+ @param Width die horizontale Bildschirmauflösung in Pixeln
+ @param Height die vertikale Bildschirmauflösung in Pixeln
+ @param Die Anzahl an Framebuffern, die eingesetzt wird (Backbuffer + Primary).
+ */
+ RenderObjectManager(int width, int height, int framebufferCount);
+ virtual ~RenderObjectManager();
+
+ // Interface
+ // ---------
+ /**
+ @brief Initialisiert den Manager für einen neuen Frame.
+ @remark Alle Veränderungen an Objekten müssen nach einem Aufruf dieser Methode geschehen, damit sichergestellt ist, dass diese
+ visuell umgesetzt werden.<br>
+ Mit dem Aufruf dieser Methode werden die Rückgabewerte von GetUpdateRects() und GetUpdateRectCount() auf ihre Startwerte
+ zurückgesetzt. Wenn man also mit diesen Werten arbeiten möchten, muss man dies nach einem Aufruf von Render() und vor
+ einem Aufruf von StartFrame() tun.
+ */
+ void startFrame();
+ /**
+ @brief Rendert alle Objekte die sich während des letzten Aufrufes von Render() verändert haben.
+ @return Gibt false zurück, falls das Rendern fehlgeschlagen ist.
+ */
+ bool render();
+ /**
+ @brief Gibt einen Pointer auf die Wurzel des Objektbaumes zurück.
+ */
+ RenderObjectPtr<RenderObject> getTreeRoot() {
+ return _rootPtr;
+ }
+ /**
+ @brief Fügt ein BS_TimedRenderObject in die Liste der zeitabhängigen Render-Objekte.
+
+ Alle Objekte die sich in dieser Liste befinden werden vor jedem Frame über die seit dem letzten Frame
+ vergangene Zeit informiert, so dass sich ihren Zustand zeitabhängig verändern können.
+
+ @param RenderObject das einzufügende BS_TimedRenderObject
+ */
+ void attatchTimedRenderObject(RenderObjectPtr<TimedRenderObject> pRenderObject);
+ /**
+ @brief Entfernt ein BS_TimedRenderObject aus der Liste für zeitabhängige Render-Objekte.
+ */
+ void detatchTimedRenderObject(RenderObjectPtr<TimedRenderObject> pRenderObject);
+
+ virtual bool persist(OutputPersistenceBlock &writer);
+ virtual bool unpersist(InputPersistenceBlock &reader);
+
+private:
+ bool _frameStarted;
+ typedef Common::Array<RenderObjectPtr<TimedRenderObject> > RenderObjectList;
+ RenderObjectList _timedRenderObjects;
+
+ // RenderObject-Tree Variablen
+ // ---------------------------
+ // Der Baum legt die hierachische Ordnung der BS_RenderObjects fest.
+ // Zu weiteren Informationen siehe: "renderobject.h"
+ RenderObjectPtr<RenderObject> _rootPtr; // Die Wurzel der Baumes
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/gfx/renderobjectptr.h b/engines/sword25/gfx/renderobjectptr.h
new file mode 100644
index 0000000000..c22c6e83e7
--- /dev/null
+++ b/engines/sword25/gfx/renderobjectptr.h
@@ -0,0 +1,79 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#ifndef SWORD25_RENDER_OBJECT_PTR_H
+#define SWORD25_RENDER_OBJECT_PTR_H
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "sword25/kernel/common.h"
+#include "sword25/gfx/renderobjectregistry.h"
+
+namespace Sword25 {
+
+class RenderObject;
+
+template<class T>
+class RenderObjectPtr {
+public:
+ RenderObjectPtr() : _handle(0) {}
+
+ RenderObjectPtr(uint handle) : _handle(handle) {}
+
+ T *operator->() const {
+ return static_cast<T *>(RenderObjectRegistry::instance().resolveHandle(_handle));
+ }
+
+ bool operator==(const RenderObjectPtr<T> & other) {
+ return _handle == other._handle;
+ }
+
+ bool isValid() const {
+ return RenderObjectRegistry::instance().resolveHandle(_handle) != 0;
+ }
+
+ void erase() {
+ delete static_cast<T *>(RenderObjectRegistry::instance().resolveHandle(_handle));
+ _handle = 0;
+ }
+
+private:
+ uint _handle;
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/gfx/renderobjectregistry.cpp b/engines/sword25/gfx/renderobjectregistry.cpp
new file mode 100644
index 0000000000..c52e2f1a45
--- /dev/null
+++ b/engines/sword25/gfx/renderobjectregistry.cpp
@@ -0,0 +1,51 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#include "sword25/gfx/renderobjectregistry.h"
+
+DECLARE_SINGLETON(Sword25::RenderObjectRegistry)
+
+namespace Sword25 {
+
+#define BS_LOG_PREFIX "RENDEROBJECTREGISTRY"
+
+void RenderObjectRegistry::logErrorLn(const char *message) const {
+ BS_LOG_ERRORLN(message);
+}
+
+void RenderObjectRegistry::logWarningLn(const char *message) const {
+ BS_LOG_WARNINGLN(message);
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/gfx/renderobjectregistry.h b/engines/sword25/gfx/renderobjectregistry.h
new file mode 100644
index 0000000000..357d041068
--- /dev/null
+++ b/engines/sword25/gfx/renderobjectregistry.h
@@ -0,0 +1,57 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#ifndef SWORD25_RENDEROBJECTREGISTRY_H
+#define SWORD25_RENDEROBJECTREGISTRY_H
+
+#include "sword25/kernel/common.h"
+#include "sword25/kernel/objectregistry.h"
+
+#include "common/singleton.h"
+
+namespace Sword25 {
+
+class RenderObject;
+
+class RenderObjectRegistry :
+ public ObjectRegistry<RenderObject>,
+ public Common::Singleton<RenderObjectRegistry> {
+private:
+ virtual void logErrorLn(const char *message) const;
+ virtual void logWarningLn(const char *message) const;
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/gfx/rootrenderobject.h b/engines/sword25/gfx/rootrenderobject.h
new file mode 100644
index 0000000000..e4e3fba3c8
--- /dev/null
+++ b/engines/sword25/gfx/rootrenderobject.h
@@ -0,0 +1,72 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#ifndef SWORD25_ROOTRENDEROBJECT_H
+#define SWORD25_ROOTRENDEROBJECT_H
+
+// Includes
+#include "sword25/kernel/common.h"
+#include "sword25/gfx/renderobject.h"
+
+namespace Sword25 {
+
+// -----------------------------------------------------------------------------
+// Forward Declarations
+// -----------------------------------------------------------------------------
+
+class Kernel;
+
+// Klassendefinition
+class RenderObjectManager;
+
+class RootRenderObject : public RenderObject {
+ friend class RenderObjectManager;
+
+private:
+ RootRenderObject(RenderObjectManager *managerPtr, int width, int height) :
+ RenderObject(RenderObjectPtr<RenderObject>(), TYPE_ROOT) {
+ _managerPtr = managerPtr;
+ _width = width;
+ _height = height;
+ }
+
+protected:
+ virtual bool doRender() {
+ return true;
+ }
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/gfx/screenshot.cpp b/engines/sword25/gfx/screenshot.cpp
new file mode 100644
index 0000000000..88417b72c5
--- /dev/null
+++ b/engines/sword25/gfx/screenshot.cpp
@@ -0,0 +1,185 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+// Disable symbol overrides so that we can use png.h
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
+#define BS_LOG_PREFIX "SCREENSHOT"
+
+#include "common/system.h"
+#include "common/savefile.h"
+#include "sword25/gfx/screenshot.h"
+#include "sword25/kernel/filesystemutil.h"
+#include <png.h>
+
+namespace Sword25 {
+
+#include "common/pack-start.h"
+struct RGB_PIXEL {
+ byte red;
+ byte green;
+ byte blue;
+} PACKED_STRUCT;
+#include "common/pack-end.h"
+
+void userWriteFn(png_structp png_ptr, png_bytep data, png_size_t length) {
+ static_cast<Common::WriteStream *>(png_get_io_ptr(png_ptr))->write(data, length);
+}
+
+void userFlushFn(png_structp png_ptr) {
+}
+
+bool Screenshot::saveToFile(Graphics::Surface *data, Common::WriteStream *stream) {
+ // Reserve buffer space
+ RGB_PIXEL *pixelBuffer = new RGB_PIXEL[data->w * data->h];
+
+ // Convert the RGBA data to RGB
+ const byte *pSrc = (const byte *)data->getBasePtr(0, 0);
+ RGB_PIXEL *pDest = pixelBuffer;
+
+ for (uint y = 0; y < data->h; y++) {
+ for (uint x = 0; x < data->w; x++) {
+ uint32 srcPixel = READ_LE_UINT32(pSrc);
+ pSrc += sizeof(uint32);
+ pDest->red = (srcPixel >> 16) & 0xff;
+ pDest->green = (srcPixel >> 8) & 0xff;
+ pDest->blue = srcPixel & 0xff;
+ ++pDest;
+ }
+ }
+
+ png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if (!png_ptr)
+ error("Could not create PNG write-struct.");
+
+ png_infop info_ptr = png_create_info_struct(png_ptr);
+ if (!info_ptr)
+ error("Could not create PNG info-struct.");
+
+ // The compression buffer must be large enough to the entire image.
+ // This ensures that only an IDAT chunk is created.
+ // When buffer size is used 110% of the raw data size to be sure.
+ png_set_compression_buffer_size(png_ptr, (data->w * data->h * 3 * 110) / 100);
+
+ // Initialise PNG-Info structure
+ png_set_IHDR(png_ptr, info_ptr,
+ data->w, // Width
+ data->h, // Height
+ 8, // Bits depth
+ PNG_COLOR_TYPE_RGB, // Colour type
+ PNG_INTERLACE_NONE, // No interlacing
+ PNG_COMPRESSION_TYPE_DEFAULT, // Compression type
+ PNG_FILTER_TYPE_DEFAULT); // Filter Type
+
+ // Rowpointer erstellen
+ png_bytep *rowPointers = new png_bytep[data->h];
+ for (uint i = 0; i < data->h; i++) {
+ rowPointers[i] = (png_bytep)&pixelBuffer[data->w * i];
+ }
+ png_set_rows(png_ptr, info_ptr, &rowPointers[0]);
+
+ // Write out the png data to the file
+ png_set_write_fn(png_ptr, (void *)stream, userWriteFn, userFlushFn);
+ png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
+
+ png_destroy_write_struct(&png_ptr, &info_ptr);
+
+ delete[] pixelBuffer;
+ delete[] rowPointers;
+
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+Common::MemoryReadStream *Screenshot::createThumbnail(Graphics::Surface *data) {
+ // This method takes a screen image with a dimension of 800x600, and creates a screenshot with a dimension of 200x125.
+ // First 50 pixels are cut off the top and bottom (the interface boards in the game). The remaining image of 800x500
+ // will be on a 16th of its size, reduced by being handed out in 4x4 pixel blocks and the average of each block
+ // generates a pixel of the target image. Finally, the result as a PNG file is stored as a file.
+
+ // The source image must be 800x600.
+ if (data->w != 800 || data->h != 600 || data->bytesPerPixel != 4) {
+ BS_LOG_ERRORLN("The sreenshot dimensions have to be 800x600 in order to be saved as a thumbnail.");
+ return false;
+ }
+
+ // Buffer for the output thumbnail
+ Graphics::Surface thumbnail;
+ thumbnail.create(200, 125, 4);
+
+ // Über das Zielbild iterieren und einen Pixel zur Zeit berechnen.
+ uint x, y;
+ x = y = 0;
+
+ for (byte *pDest = (byte *)thumbnail.pixels; pDest < ((byte *)thumbnail.pixels + thumbnail.pitch * thumbnail.h); ) {
+ // Get an average over a 4x4 pixel block in the source image
+ int alpha, red, green, blue;
+ alpha = red = green = blue = 0;
+ for (int j = 0; j < 4; ++j) {
+ const uint32 *srcP = (const uint32 *)data->getBasePtr(x * 4, y * 4 + j + 50);
+ for (int i = 0; i < 4; ++i) {
+ uint32 pixel = READ_LE_UINT32(srcP + i);
+ alpha += (pixel >> 24);
+ red += (pixel >> 16) & 0xff;
+ green += (pixel >> 8) & 0xff;
+ blue += pixel & 0xff;
+ }
+ }
+
+ // Write target pixel
+ *pDest++ = blue / 16;
+ *pDest++ = green / 16;
+ *pDest++ = red / 16;
+ *pDest++ = alpha / 16;
+
+ // Move to next block
+ ++x;
+ if (x == thumbnail.w) {
+ x = 0;
+ ++y;
+ }
+ }
+
+ // Create a PNG representation of the thumbnail data
+ Common::MemoryWriteStreamDynamic *stream = new Common::MemoryWriteStreamDynamic();
+ saveToFile(&thumbnail, stream);
+
+ // Output a MemoryReadStream that encompasses the written data
+ Common::MemoryReadStream *result = new Common::MemoryReadStream(stream->getData(), stream->size(),
+ DisposeAfterUse::YES);
+ return result;
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/gfx/screenshot.h b/engines/sword25/gfx/screenshot.h
new file mode 100644
index 0000000000..eefaa1bca6
--- /dev/null
+++ b/engines/sword25/gfx/screenshot.h
@@ -0,0 +1,51 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#ifndef SWORD25_SCREENSHOT_H
+#define SWORD25_SCREENSHOT_H
+
+#include "graphics/surface.h"
+#include "sword25/kernel/common.h"
+
+namespace Sword25 {
+
+class Screenshot {
+public:
+ static bool saveToFile(Graphics::Surface *data, Common::WriteStream *stream);
+ static Common::MemoryReadStream *createThumbnail(Graphics::Surface *data);
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/gfx/staticbitmap.cpp b/engines/sword25/gfx/staticbitmap.cpp
new file mode 100644
index 0000000000..3019fe0ac1
--- /dev/null
+++ b/engines/sword25/gfx/staticbitmap.cpp
@@ -0,0 +1,185 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#include "sword25/gfx/staticbitmap.h"
+#include "sword25/gfx/bitmapresource.h"
+#include "sword25/package/packagemanager.h"
+#include "sword25/kernel/outputpersistenceblock.h"
+#include "sword25/kernel/inputpersistenceblock.h"
+
+namespace Sword25 {
+
+#define BS_LOG_PREFIX "STATICBITMAP"
+
+StaticBitmap::StaticBitmap(RenderObjectPtr<RenderObject> parentPtr, const Common::String &filename) :
+ Bitmap(parentPtr, TYPE_STATICBITMAP) {
+ // Das BS_Bitmap konnte nicht erzeugt werden, daher muss an dieser Stelle abgebrochen werden.
+ if (!_initSuccess)
+ return;
+
+ _initSuccess = initBitmapResource(filename);
+}
+
+StaticBitmap::StaticBitmap(InputPersistenceBlock &reader, RenderObjectPtr<RenderObject> parentPtr, uint handle) :
+ Bitmap(parentPtr, TYPE_STATICBITMAP, handle) {
+ _initSuccess = unpersist(reader);
+}
+
+bool StaticBitmap::initBitmapResource(const Common::String &filename) {
+ // Bild-Resource laden
+ Resource *resourcePtr = Kernel::getInstance()->getResourceManager()->requestResource(filename);
+ if (!resourcePtr) {
+ BS_LOG_ERRORLN("Could not request resource \"%s\".", filename.c_str());
+ return false;
+ }
+ if (resourcePtr->getType() != Resource::TYPE_BITMAP) {
+ BS_LOG_ERRORLN("Requested resource \"%s\" is not a bitmap.", filename.c_str());
+ return false;
+ }
+
+ BitmapResource *bitmapPtr = static_cast<BitmapResource *>(resourcePtr);
+
+ // Den eindeutigen Dateinamen zum späteren Referenzieren speichern
+ _resourceFilename = bitmapPtr->getFileName();
+
+ // RenderObject Eigenschaften aktualisieren
+ _originalWidth = _width = bitmapPtr->getWidth();
+ _originalHeight = _height = bitmapPtr->getHeight();
+
+ // Bild-Resource freigeben
+ bitmapPtr->release();
+
+ return true;
+}
+
+StaticBitmap::~StaticBitmap() {
+}
+
+bool StaticBitmap::doRender() {
+ // Bitmap holen
+ Resource *resourcePtr = Kernel::getInstance()->getResourceManager()->requestResource(_resourceFilename);
+ BS_ASSERT(resourcePtr);
+ BS_ASSERT(resourcePtr->getType() == Resource::TYPE_BITMAP);
+ BitmapResource *bitmapResourcePtr = static_cast<BitmapResource *>(resourcePtr);
+
+ // Framebufferobjekt holen
+ GraphicEngine *gfxPtr = Kernel::getInstance()->getGfx();
+ BS_ASSERT(gfxPtr);
+
+ // Bitmap zeichnen
+ bool result;
+ if (_scaleFactorX == 1.0f && _scaleFactorY == 1.0f) {
+ result = bitmapResourcePtr->blit(_absoluteX, _absoluteY,
+ (_flipV ? BitmapResource::FLIP_V : 0) |
+ (_flipH ? BitmapResource::FLIP_H : 0),
+ 0, _modulationColor, -1, -1);
+ } else {
+ result = bitmapResourcePtr->blit(_absoluteX, _absoluteY,
+ (_flipV ? BitmapResource::FLIP_V : 0) |
+ (_flipH ? BitmapResource::FLIP_H : 0),
+ 0, _modulationColor, _width, _height);
+ }
+
+ // Resource freigeben
+ bitmapResourcePtr->release();
+
+ return result;
+}
+
+uint StaticBitmap::getPixel(int x, int y) const {
+ BS_ASSERT(x >= 0 && x < _width);
+ BS_ASSERT(y >= 0 && y < _height);
+
+ Resource *pResource = Kernel::getInstance()->getResourceManager()->requestResource(_resourceFilename);
+ BS_ASSERT(pResource->getType() == Resource::TYPE_BITMAP);
+ BitmapResource *pBitmapResource = static_cast<BitmapResource *>(pResource);
+ uint result = pBitmapResource->getPixel(x, y);
+ pResource->release();
+ return result;
+}
+
+bool StaticBitmap::setContent(const byte *pixeldata, uint size, uint offset, uint stride) {
+ BS_LOG_ERRORLN("SetContent() ist not supported with this object.");
+ return false;
+}
+
+bool StaticBitmap::isAlphaAllowed() const {
+ Resource *pResource = Kernel::getInstance()->getResourceManager()->requestResource(_resourceFilename);
+ BS_ASSERT(pResource->getType() == Resource::TYPE_BITMAP);
+ bool result = static_cast<BitmapResource *>(pResource)->isAlphaAllowed();
+ pResource->release();
+ return result;
+}
+
+bool StaticBitmap::isColorModulationAllowed() const {
+ Resource *pResource = Kernel::getInstance()->getResourceManager()->requestResource(_resourceFilename);
+ BS_ASSERT(pResource->getType() == Resource::TYPE_BITMAP);
+ bool result = static_cast<BitmapResource *>(pResource)->isColorModulationAllowed();
+ pResource->release();
+ return result;
+}
+
+bool StaticBitmap::isScalingAllowed() const {
+ Resource *pResource = Kernel::getInstance()->getResourceManager()->requestResource(_resourceFilename);
+ BS_ASSERT(pResource->getType() == Resource::TYPE_BITMAP);
+ bool result = static_cast<BitmapResource *>(pResource)->isScalingAllowed();
+ pResource->release();
+ return result;
+}
+
+bool StaticBitmap::persist(OutputPersistenceBlock &writer) {
+ bool result = true;
+
+ result &= Bitmap::persist(writer);
+ writer.writeString(_resourceFilename);
+
+ result &= RenderObject::persistChildren(writer);
+
+ return result;
+}
+
+bool StaticBitmap::unpersist(InputPersistenceBlock &reader) {
+ bool result = true;
+
+ result &= Bitmap::unpersist(reader);
+ Common::String resourceFilename;
+ reader.readString(resourceFilename);
+ result &= initBitmapResource(resourceFilename);
+
+ result &= RenderObject::unpersistChildren(reader);
+
+ return reader.isGood() && result;
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/gfx/staticbitmap.h b/engines/sword25/gfx/staticbitmap.h
new file mode 100644
index 0000000000..b5b4c4f5a2
--- /dev/null
+++ b/engines/sword25/gfx/staticbitmap.h
@@ -0,0 +1,81 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#ifndef SWORD25_STATIC_BITMAP_H
+#define SWORD25_STATIC_BITMAP_H
+
+#include "sword25/kernel/common.h"
+#include "sword25/gfx/bitmap.h"
+
+namespace Sword25 {
+
+class StaticBitmap : public Bitmap {
+ friend class RenderObject;
+
+private:
+ /**
+ @remark Filename muss absoluter Pfad sein
+ */
+ StaticBitmap(RenderObjectPtr<RenderObject> parentPtr, const Common::String &filename);
+ StaticBitmap(InputPersistenceBlock &reader, RenderObjectPtr<RenderObject> parentPtr, uint handle);
+
+public:
+ virtual ~StaticBitmap();
+
+ virtual uint getPixel(int x, int y) const;
+
+ virtual bool setContent(const byte *pixeldata, uint size, uint offset, uint stride);
+
+ virtual bool isScalingAllowed() const;
+ virtual bool isAlphaAllowed() const;
+ virtual bool isColorModulationAllowed() const;
+ virtual bool isSetContentAllowed() const {
+ return false;
+ }
+
+ virtual bool persist(OutputPersistenceBlock &writer);
+ virtual bool unpersist(InputPersistenceBlock &reader);
+
+protected:
+ virtual bool doRender();
+
+private:
+ Common::String _resourceFilename;
+
+ bool initBitmapResource(const Common::String &filename);
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/gfx/text.cpp b/engines/sword25/gfx/text.cpp
new file mode 100644
index 0000000000..b8e12ba570
--- /dev/null
+++ b/engines/sword25/gfx/text.cpp
@@ -0,0 +1,358 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+// TODO:
+// Entweder Fontfile absolut abspeichern, oder Verzeichniswechseln verbieten
+// Eine relative Fontfile-Angabe könnte verwandt werden nachdem das Verzeichnis bereits gewechselt wurde und die Datei würde nicht mehr gefunden
+
+#include "sword25/kernel/kernel.h"
+#include "sword25/kernel/outputpersistenceblock.h"
+#include "sword25/kernel/inputpersistenceblock.h"
+#include "sword25/gfx/fontresource.h"
+#include "sword25/gfx/bitmapresource.h"
+
+#include "sword25/gfx/text.h"
+
+namespace Sword25 {
+
+#define BS_LOG_PREFIX "TEXT"
+
+namespace {
+const uint AUTO_WRAP_THRESHOLD_DEFAULT = 300;
+}
+
+Text::Text(RenderObjectPtr<RenderObject> parentPtr) :
+ RenderObject(parentPtr, RenderObject::TYPE_TEXT),
+ _modulationColor(0xffffffff),
+ _autoWrap(false),
+ _autoWrapThreshold(AUTO_WRAP_THRESHOLD_DEFAULT) {
+
+}
+
+Text::Text(InputPersistenceBlock &reader, RenderObjectPtr<RenderObject> parentPtr, uint handle) :
+ RenderObject(parentPtr, TYPE_TEXT, handle),
+ // Temporarily set fields prior to unpersisting actual values
+ _modulationColor(0xffffffff),
+ _autoWrap(false),
+ _autoWrapThreshold(AUTO_WRAP_THRESHOLD_DEFAULT) {
+
+ // Unpersist the fields
+ _initSuccess = unpersist(reader);
+}
+
+bool Text::setFont(const Common::String &font) {
+ // Font precachen.
+ if (getResourceManager()->precacheResource(font)) {
+ _font = font;
+ updateFormat();
+ forceRefresh();
+ return true;
+ } else {
+ BS_LOG_ERRORLN("Could not precache font \"%s\". Font probably does not exist.", font.c_str());
+ return false;
+ }
+
+}
+
+void Text::setText(const Common::String &text) {
+ _text = text;
+ updateFormat();
+ forceRefresh();
+}
+
+void Text::setColor(uint modulationColor) {
+ uint newModulationColor = (modulationColor & 0x00ffffff) | (_modulationColor & 0xff000000);
+ if (newModulationColor != _modulationColor) {
+ _modulationColor = newModulationColor;
+ forceRefresh();
+ }
+}
+
+void Text::setAlpha(int alpha) {
+ BS_ASSERT(alpha >= 0 && alpha < 256);
+ uint newModulationColor = (_modulationColor & 0x00ffffff) | alpha << 24;
+ if (newModulationColor != _modulationColor) {
+ _modulationColor = newModulationColor;
+ forceRefresh();
+ }
+}
+
+void Text::setAutoWrap(bool autoWrap) {
+ if (autoWrap != _autoWrap) {
+ _autoWrap = autoWrap;
+ updateFormat();
+ forceRefresh();
+ }
+}
+
+void Text::setAutoWrapThreshold(uint autoWrapThreshold) {
+ if (autoWrapThreshold != _autoWrapThreshold) {
+ _autoWrapThreshold = autoWrapThreshold;
+ updateFormat();
+ forceRefresh();
+ }
+}
+
+bool Text::doRender() {
+ // Font-Resource locken.
+ FontResource *fontPtr = lockFontResource();
+ if (!fontPtr)
+ return false;
+
+ // Charactermap-Resource locken.
+ ResourceManager *rmPtr = getResourceManager();
+ BitmapResource *charMapPtr;
+ {
+ Resource *pResource = rmPtr->requestResource(fontPtr->getCharactermapFileName());
+ if (!pResource) {
+ BS_LOG_ERRORLN("Could not request resource \"%s\".", fontPtr->getCharactermapFileName().c_str());
+ return false;
+ }
+ if (pResource->getType() != Resource::TYPE_BITMAP) {
+ BS_LOG_ERRORLN("Requested resource \"%s\" is not a bitmap.", fontPtr->getCharactermapFileName().c_str());
+ return false;
+ }
+
+ charMapPtr = static_cast<BitmapResource *>(pResource);
+ }
+
+ // Framebufferobjekt holen.
+ GraphicEngine *gfxPtr = Kernel::getInstance()->getGfx();
+ BS_ASSERT(gfxPtr);
+
+ bool result = true;
+ Common::Array<Line>::iterator iter = _lines.begin();
+ for (; iter != _lines.end(); ++iter) {
+ // Feststellen, ob überhaupt Buchstaben der aktuellen Zeile vom Update betroffen sind.
+ Common::Rect checkRect = (*iter).bbox;
+ checkRect.translate(_absoluteX, _absoluteY);
+
+ // Jeden Buchstaben einzeln Rendern.
+ int curX = _absoluteX + (*iter).bbox.left;
+ int curY = _absoluteY + (*iter).bbox.top;
+ for (uint i = 0; i < (*iter).text.size(); ++i) {
+ Common::Rect curRect = fontPtr->getCharacterRect((byte)(*iter).text[i]);
+
+ Common::Rect renderRect(curX, curY, curX + curRect.width(), curY + curRect.height());
+ int renderX = curX + (renderRect.left - renderRect.left);
+ int renderY = curY + (renderRect.top - renderRect.top);
+ renderRect.translate(curRect.left - curX, curRect.top - curY);
+ result = charMapPtr->blit(renderX, renderY, Image::FLIP_NONE, &renderRect, _modulationColor);
+ if (!result)
+ break;
+
+ curX += curRect.width() + fontPtr->getGapWidth();
+ }
+ }
+
+ // Charactermap-Resource freigeben.
+ charMapPtr->release();
+
+ // Font-Resource freigeben.
+ fontPtr->release();
+
+ return result;
+}
+
+ResourceManager *Text::getResourceManager() {
+ // Pointer auf den Resource-Manager holen.
+ return Kernel::getInstance()->getResourceManager();
+}
+
+FontResource *Text::lockFontResource() {
+ ResourceManager *rmPtr = getResourceManager();
+
+ // Font-Resource locken.
+ FontResource *fontPtr;
+ {
+ Resource *resourcePtr = rmPtr->requestResource(_font);
+ if (!resourcePtr) {
+ BS_LOG_ERRORLN("Could not request resource \"%s\".", _font.c_str());
+ return NULL;
+ }
+ if (resourcePtr->getType() != Resource::TYPE_FONT) {
+ BS_LOG_ERRORLN("Requested resource \"%s\" is not a font.", _font.c_str());
+ return NULL;
+ }
+
+ fontPtr = static_cast<FontResource *>(resourcePtr);
+ }
+
+ return fontPtr;
+}
+
+void Text::updateFormat() {
+ FontResource *fontPtr = lockFontResource();
+ BS_ASSERT(fontPtr);
+
+ updateMetrics(*fontPtr);
+
+ _lines.resize(1);
+ if (_autoWrap && (uint) _width >= _autoWrapThreshold && _text.size() >= 2) {
+ _width = 0;
+ uint curLineWidth = 0;
+ uint curLineHeight = 0;
+ uint curLine = 0;
+ uint tempLineWidth = 0;
+ uint lastSpace = 0; // we need at least 1 space character to start a new line...
+ _lines[0].text = "";
+ for (uint i = 0; i < _text.size(); ++i) {
+ uint j;
+ tempLineWidth = 0;
+ lastSpace = 0;
+ for (j = i; j < _text.size(); ++j) {
+ if ((byte)_text[j] == ' ')
+ lastSpace = j;
+
+ const Common::Rect &curCharRect = fontPtr->getCharacterRect((byte)_text[j]);
+ tempLineWidth += curCharRect.width();
+ tempLineWidth += fontPtr->getGapWidth();
+
+ if ((tempLineWidth >= _autoWrapThreshold) && (lastSpace > 0))
+ break;
+ }
+
+ if (j == _text.size()) // everything in 1 line.
+ lastSpace = _text.size();
+
+ curLineWidth = 0;
+ curLineHeight = 0;
+ for (j = i; j < lastSpace; ++j) {
+ _lines[curLine].text += _text[j];
+
+ const Common::Rect &curCharRect = fontPtr->getCharacterRect((byte)_text[j]);
+ curLineWidth += curCharRect.width();
+ curLineWidth += fontPtr->getGapWidth();
+ if ((uint)curCharRect.height() > curLineHeight)
+ curLineHeight = curCharRect.height();
+ }
+
+ _lines[curLine].bbox.right = curLineWidth;
+ _lines[curLine].bbox.bottom = curLineHeight;
+ if ((uint)_width < curLineWidth)
+ _width = curLineWidth;
+
+ if (lastSpace < _text.size()) {
+ ++curLine;
+ BS_ASSERT(curLine == _lines.size());
+ _lines.resize(curLine + 1);
+ _lines[curLine].text = "";
+ }
+
+ i = lastSpace;
+ }
+
+ // Bounding-Box der einzelnen Zeilen relativ zur ersten festlegen (vor allem zentrieren).
+ _height = 0;
+ Common::Array<Line>::iterator iter = _lines.begin();
+ for (; iter != _lines.end(); ++iter) {
+ Common::Rect &bbox = (*iter).bbox;
+ bbox.left = (_width - bbox.right) / 2;
+ bbox.right = bbox.left + bbox.right;
+ bbox.top = (iter - _lines.begin()) * fontPtr->getLineHeight();
+ bbox.bottom = bbox.top + bbox.bottom;
+ _height += bbox.height();
+ }
+ } else {
+ // Keine automatische Formatierung, also wird der gesamte Text in nur eine Zeile kopiert.
+ _lines[0].text = _text;
+ _lines[0].bbox = Common::Rect(0, 0, _width, _height);
+ }
+
+ fontPtr->release();
+}
+
+void Text::updateMetrics(FontResource &fontResource) {
+ _width = 0;
+ _height = 0;
+
+ for (uint i = 0; i < _text.size(); ++i) {
+ const Common::Rect &curRect = fontResource.getCharacterRect((byte)_text[i]);
+ _width += curRect.width();
+ if (i != _text.size() - 1)
+ _width += fontResource.getGapWidth();
+ if (_height < curRect.height())
+ _height = curRect.height();
+ }
+}
+
+bool Text::persist(OutputPersistenceBlock &writer) {
+ bool result = true;
+
+ result &= RenderObject::persist(writer);
+
+ writer.write(_modulationColor);
+ writer.writeString(_font);
+ writer.writeString(_text);
+ writer.write(_autoWrap);
+ writer.write(_autoWrapThreshold);
+
+ result &= RenderObject::persistChildren(writer);
+
+ return result;
+}
+
+bool Text::unpersist(InputPersistenceBlock &reader) {
+ bool result = true;
+
+ result &= RenderObject::unpersist(reader);
+
+ // Farbe und Alpha einlesen.
+ reader.read(_modulationColor);
+
+ // Beim Laden der anderen Member werden die Set-Methoden benutzt statt der tatsächlichen Member.
+ // So wird das Layout automatisch aktualisiert und auch alle anderen notwendigen Methoden ausgeführt.
+
+ Common::String font;
+ reader.readString(font);
+ setFont(font);
+
+ Common::String text;
+ reader.readString(text);
+ setText(text);
+
+ bool autoWrap;
+ reader.read(autoWrap);
+ setAutoWrap(autoWrap);
+
+ uint autoWrapThreshold;
+ reader.read(autoWrapThreshold);
+ setAutoWrapThreshold(autoWrapThreshold);
+
+ result &= RenderObject::unpersistChildren(reader);
+
+ return reader.isGood() && result;
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/gfx/text.h b/engines/sword25/gfx/text.h
new file mode 100644
index 0000000000..42c1cd7c5d
--- /dev/null
+++ b/engines/sword25/gfx/text.h
@@ -0,0 +1,169 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#ifndef SWORD25_TEXT_H
+#define SWORD25_TEXT_H
+
+#include "sword25/kernel/common.h"
+#include "common/rect.h"
+#include "sword25/gfx/renderobject.h"
+
+namespace Sword25 {
+
+class Kernel;
+class FontResource;
+class ResourceManager;
+
+class Text : public RenderObject {
+ friend class RenderObject;
+
+public:
+ /**
+ @brief Setzt den Font mit dem der Text dargestellt werden soll.
+ @param Font der Dateiname der Fontdatei.
+ @return Gibt false zurück, wenn der Font nicht gefunden wurde.
+ */
+ bool setFont(const Common::String &font);
+
+ /**
+ @brief Setzt den darzustellenden Text.
+ @param Text der darzustellende Text
+ */
+ void setText(const Common::String &text);
+
+ /**
+ @brief Setzt den Alphawert des Textes.
+ @param Alpha der neue Alphawert des Textes (0 = keine Deckung, 255 = volle Deckung).
+ */
+ void setAlpha(int alpha);
+
+ /**
+ @brief Legt fest, ob der Text automatisch umgebrochen werden soll.
+
+ Wenn dieses Attribut auf true gesetzt ist, wird der Text umgebrochen, sofern er länger als GetAutoWrapThreshold() ist.
+
+ @param AutoWrap gibt an, ob der automatische Umbruch aktiviert oder deaktiviert werden soll.
+ @remark Dieses Attribut wird mit dem Wert false initialisiert.
+ */
+ void setAutoWrap(bool autoWrap);
+
+ /**
+ @brief Legt die Längengrenze des Textes in Pixeln fest, ab der ein automatischer Zeilenumbruch vorgenommen wird.
+ @remark Dieses Attribut wird mit dem Wert 300 initialisiert.
+ @remark Eine automatische Formatierung wird nur vorgenommen, wenn diese durch einen Aufruf von SetAutoWrap() aktiviert wurde.
+ */
+ void setAutoWrapThreshold(uint autoWrapThreshold);
+
+ /**
+ @brief Gibt den dargestellten Text zurück.
+ */
+ const Common::String &getText() {
+ return _text;
+ }
+
+ /**
+ @brief Gibt den Namen das momentan benutzten Fonts zurück.
+ */
+ const Common::String &getFont() {
+ return _font;
+ }
+
+ /**
+ @brief Setzt die Farbe des Textes.
+ @param Color eine 24-Bit RGB Farbe, die die Farbe des Textes festlegt.
+ */
+ void setColor(uint modulationColor);
+
+ /**
+ @brief Gibt den Alphawert des Textes zurück.
+ @return Der Alphawert des Textes (0 = keine Deckung, 255 = volle Deckung).
+ */
+ int getAlpha() const {
+ return _modulationColor >> 24;
+ }
+
+ /**
+ @brief Gibt die Farbe des Textes zurück.
+ @return Eine 24-Bit RGB Farbe, die die Farbe des Textes angibt.
+ */
+ int getColor() const {
+ return _modulationColor & 0x00ffffff;
+ }
+
+ /**
+ @brief Gibt zurück, ob die automatische Formatierung aktiviert ist.
+ */
+ bool isAutoWrapActive() const {
+ return _autoWrap;
+ }
+
+ /**
+ @brief Gibt die Längengrenze des Textes in Pixeln zurück, ab der eine automatische Formatierung vorgenommen wird.
+ */
+ uint getAutoWrapThreshold() const {
+ return _autoWrapThreshold;
+ }
+
+ virtual bool persist(OutputPersistenceBlock &writer);
+ virtual bool unpersist(InputPersistenceBlock &reader);
+
+protected:
+ virtual bool doRender();
+
+private:
+ Text(RenderObjectPtr<RenderObject> parentPtr);
+ Text(InputPersistenceBlock &reader, RenderObjectPtr<RenderObject> parentPtr, uint handle);
+
+ uint _modulationColor;
+ Common::String _font;
+ Common::String _text;
+ bool _autoWrap;
+ uint _autoWrapThreshold;
+
+ struct Line {
+ Common::Rect bbox;
+ Common::String text;
+ };
+
+ Common::Array<Line> _lines;
+
+ void updateFormat();
+ void updateMetrics(FontResource &fontResource);
+ ResourceManager *getResourceManager();
+ FontResource *lockFontResource();
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/gfx/timedrenderobject.cpp b/engines/sword25/gfx/timedrenderobject.cpp
new file mode 100644
index 0000000000..eaa9b90d26
--- /dev/null
+++ b/engines/sword25/gfx/timedrenderobject.cpp
@@ -0,0 +1,52 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#include "sword25/gfx/timedrenderobject.h"
+
+#include "sword25/gfx/renderobjectmanager.h"
+
+namespace Sword25 {
+
+TimedRenderObject::TimedRenderObject(RenderObjectPtr<RenderObject> pParent, TYPES type, uint handle) :
+ RenderObject(pParent, type, handle) {
+ BS_ASSERT(getManager());
+ getManager()->attatchTimedRenderObject(this->getHandle());
+}
+
+TimedRenderObject::~TimedRenderObject() {
+ BS_ASSERT(getManager());
+ getManager()->detatchTimedRenderObject(this->getHandle());
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/gfx/timedrenderobject.h b/engines/sword25/gfx/timedrenderobject.h
new file mode 100644
index 0000000000..6fee19882a
--- /dev/null
+++ b/engines/sword25/gfx/timedrenderobject.h
@@ -0,0 +1,59 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#include "sword25/kernel/common.h"
+#include "sword25/gfx/renderobject.h"
+
+namespace Sword25 {
+
+/**
+ @brief
+*/
+
+class TimedRenderObject : public RenderObject {
+public:
+ TimedRenderObject(RenderObjectPtr<RenderObject> pParent, TYPES type, uint handle = 0);
+ ~TimedRenderObject();
+
+ /**
+ @brief Teilt dem Objekt mit, dass ein neuer Frame begonnen wird.
+
+ Diese Methode wird jeden Frame an jedem BS_TimedRenderObject aufgerufen um diesen zu ermöglichen
+ ihren Zustand Zeitabhängig zu verändern (z.B. Animationen).<br>
+ @param int TimeElapsed gibt an wie viel Zeit (in Microsekunden) seit dem letzten Frame vergangen ist.
+ */
+ virtual void frameNotification(int timeElapsed) = 0;
+};
+
+} // End of namespace Sword25