aboutsummaryrefslogtreecommitdiff
path: root/engines/zvision/graphics
diff options
context:
space:
mode:
authorRichieSams2013-11-01 00:21:08 -0500
committerRichieSams2013-11-01 02:52:57 -0500
commit5842c5098fbd00c8ab5245ba1d6ca9c2675e3da4 (patch)
treebdb138d047faae28893bdf37758940916a091a12 /engines/zvision/graphics
parent651bf899399de2b5c2d8e62a5bb4964b037950d2 (diff)
downloadscummvm-rg350-5842c5098fbd00c8ab5245ba1d6ca9c2675e3da4.tar.gz
scummvm-rg350-5842c5098fbd00c8ab5245ba1d6ca9c2675e3da4.tar.bz2
scummvm-rg350-5842c5098fbd00c8ab5245ba1d6ca9c2675e3da4.zip
ZVISION: Create a folder structure for ZVision source files
I personally used filters within my IDE, but since others are now joining the project, it was brought to my attention that some better organization would be nice.
Diffstat (limited to 'engines/zvision/graphics')
-rw-r--r--engines/zvision/graphics/render_manager.cpp526
-rw-r--r--engines/zvision/graphics/render_manager.h328
-rw-r--r--engines/zvision/graphics/render_table.cpp240
-rw-r--r--engines/zvision/graphics/render_table.h85
4 files changed, 1179 insertions, 0 deletions
diff --git a/engines/zvision/graphics/render_manager.cpp b/engines/zvision/graphics/render_manager.cpp
new file mode 100644
index 0000000000..af8ca7fd64
--- /dev/null
+++ b/engines/zvision/graphics/render_manager.cpp
@@ -0,0 +1,526 @@
+/* ScummVM - Graphic Adventure Engine
+*
+* ScummVM is the legal property of its developers, whose names
+* are too numerous to list here. Please refer to the COPYRIGHT
+* file distributed with this source distribution.
+*
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License
+* as published by the Free Software Foundation; either version 2
+* of the License, or (at your option) any later version.
+
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*
+*/
+
+#include "common/scummsys.h"
+
+#include "zvision/render_manager.h"
+
+#include "zvision/lzss_read_stream.h"
+
+#include "common/file.h"
+#include "common/system.h"
+#include "common/stream.h"
+
+#include "engines/util.h"
+
+#include "graphics/decoders/tga.h"
+
+
+namespace ZVision {
+
+RenderManager::RenderManager(OSystem *system, uint32 windowWidth, uint32 windowHeight, const Common::Rect workingWindow, const Graphics::PixelFormat pixelFormat)
+ : _system(system),
+ _workingWidth(workingWindow.width()),
+ _workingHeight(workingWindow.height()),
+ _screenCenterX(_workingWidth / 2),
+ _screenCenterY(_workingHeight / 2),
+ _workingWindow(workingWindow),
+ _pixelFormat(pixelFormat),
+ _backgroundWidth(0),
+ _backgroundHeight(0),
+ _backgroundInverseVelocity(0),
+ _backgroundOffset(0, 0),
+ _accumulatedVelocityMilliseconds(0),
+ _renderTable(_workingWidth, _workingHeight) {
+
+ _workingWindowBuffer.create(_workingWidth, _workingHeight, _pixelFormat);
+ _backBuffer.create(windowWidth, windowHeight, pixelFormat);
+}
+
+RenderManager::~RenderManager() {
+ _workingWindowBuffer.free();
+ _currentBackground.free();
+ _backBuffer.free();
+
+ for (AlphaEntryMap::iterator iter = _alphaDataEntries.begin(); iter != _alphaDataEntries.end(); ++iter) {
+ iter->_value.data->free();
+ delete iter->_value.data;
+ }
+}
+
+void RenderManager::update(uint deltaTimeInMillis) {
+ // An inverse velocity of 0 would be infinitely fast, so we'll let 0 mean no velocity.
+ if (_backgroundInverseVelocity != 0) {
+ _accumulatedVelocityMilliseconds += deltaTimeInMillis;
+
+ uint absVelocity = uint(abs(_backgroundInverseVelocity));
+
+ int numberOfSteps = 0;
+ while (_accumulatedVelocityMilliseconds >= absVelocity) {
+ _accumulatedVelocityMilliseconds -= absVelocity;
+ numberOfSteps++;
+ }
+
+ // Choose the direction of movement using the sign of the velocity
+ moveBackground(_backgroundInverseVelocity < 0 ? -numberOfSteps : numberOfSteps);
+ }
+}
+
+void RenderManager::renderBackbufferToScreen() {
+ if (!_workingWindowDirtyRect.isEmpty()) {
+ RenderTable::RenderState state = _renderTable.getRenderState();
+ if (state == RenderTable::PANORAMA || state == RenderTable::TILT) {
+ _renderTable.mutateImage((uint16 *)_workingWindowBuffer.getPixels(), (uint16 *)_backBuffer.getBasePtr(_workingWindow.left + _workingWindowDirtyRect.left, _workingWindow.top + _workingWindowDirtyRect.top), _backBuffer.w, _workingWindowDirtyRect);
+ } else {
+ _backBuffer.copyRectToSurface(_workingWindowBuffer.getBasePtr(_workingWindowDirtyRect.left, _workingWindowDirtyRect.top), _workingWindowBuffer.pitch, _workingWindow.left + _workingWindowDirtyRect.left, _workingWindow.top + _workingWindowDirtyRect.top, _workingWindowDirtyRect.width(), _workingWindowDirtyRect.height());
+ }
+
+ // Translate the working window dirty rect to screen coords
+ _workingWindowDirtyRect.translate(_workingWindow.left, _workingWindow.top);
+ // Then extend the backbuffer dirty rect to contain it
+ if (_backBufferDirtyRect.isEmpty()) {
+ _backBufferDirtyRect = _workingWindowDirtyRect;
+ } else {
+ _backBufferDirtyRect.extend(_workingWindowDirtyRect);
+ }
+
+ // Clear the dirty rect
+ _workingWindowDirtyRect = Common::Rect();
+ }
+
+ // TODO: Add menu rendering
+
+ // Render alpha entries
+ processAlphaEntries();
+
+ if (!_backBufferDirtyRect.isEmpty()) {
+ _system->copyRectToScreen(_backBuffer.getBasePtr(_backBufferDirtyRect.left, _backBufferDirtyRect.top), _backBuffer.pitch, _backBufferDirtyRect.left, _backBufferDirtyRect.top, _backBufferDirtyRect.width(), _backBufferDirtyRect.height());
+ _backBufferDirtyRect = Common::Rect();
+ }
+}
+
+void RenderManager::processAlphaEntries() {
+ // TODO: Add dirty rectangling support. AKA only draw an entry if the _backbufferDirtyRect intersects/contains the entry Rect
+
+ for (AlphaEntryMap::iterator iter = _alphaDataEntries.begin(); iter != _alphaDataEntries.end(); ++iter) {
+ uint32 destOffset = 0;
+ uint32 sourceOffset = 0;
+ uint16 *backbufferPtr = (uint16 *)_backBuffer.getBasePtr(iter->_value.destX + _workingWindow.left, iter->_value.destY + _workingWindow.top);
+ uint16 *entryPtr = (uint16 *)iter->_value.data->getPixels();
+
+ for (int32 y = 0; y < iter->_value.height; ++y) {
+ for (int32 x = 0; x < iter->_value.width; ++x) {
+ uint16 color = entryPtr[sourceOffset + x];
+ if (color != iter->_value.alphaColor) {
+ backbufferPtr[destOffset + x] = color;
+ }
+ }
+
+ destOffset += _backBuffer.w;
+ sourceOffset += iter->_value.width;
+ }
+
+ if (_backBufferDirtyRect.isEmpty()) {
+ _backBufferDirtyRect = Common::Rect(iter->_value.destX + _workingWindow.left, iter->_value.destY + _workingWindow.top, iter->_value.destX + _workingWindow.left + iter->_value.width, iter->_value.destY + _workingWindow.top + iter->_value.height);
+ } else {
+ _backBufferDirtyRect.extend(Common::Rect(iter->_value.destX + _workingWindow.left, iter->_value.destY + _workingWindow.top, iter->_value.destX + _workingWindow.left + iter->_value.width, iter->_value.destY + _workingWindow.top + iter->_value.height));
+ }
+ }
+}
+
+void RenderManager::clearWorkingWindowTo555Color(uint16 color) {
+ uint32 workingWindowSize = _workingWidth * _workingHeight;
+ byte r, g, b;
+ Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0).colorToRGB(color, r, g, b);
+ uint16 colorIn565 = _pixelFormat.RGBToColor(r, g, b);
+ uint16 *bufferPtr = (uint16 *)_workingWindowBuffer.getPixels();
+
+ for (uint32 i = 0; i < workingWindowSize; ++i) {
+ bufferPtr[i] = colorIn565;
+ }
+}
+
+void RenderManager::renderSubRectToScreen(Graphics::Surface &surface, int16 destinationX, int16 destinationY, bool wrap) {
+ int16 subRectX = 0;
+ int16 subRectY = 0;
+
+ // Take care of negative destinations
+ if (destinationX < 0) {
+ subRectX = -destinationX;
+ destinationX = 0;
+ } else if (destinationX >= surface.w) {
+ // Take care of extreme positive destinations
+ destinationX -= surface.w;
+ }
+
+ // Take care of negative destinations
+ if (destinationY < 0) {
+ subRectY = -destinationY;
+ destinationY = 0;
+ } else if (destinationY >= surface.h) {
+ // Take care of extreme positive destinations
+ destinationY -= surface.h;
+ }
+
+ if (wrap) {
+ _backgroundWidth = surface.w;
+ _backgroundHeight = surface.h;
+
+ if (destinationX > 0) {
+ // Move destinationX to 0
+ subRectX = surface.w - destinationX;
+ destinationX = 0;
+ }
+
+ if (destinationY > 0) {
+ // Move destinationY to 0
+ subRectY = surface.h - destinationY;
+ destinationY = 0;
+ }
+ }
+
+ // Clip subRect to working window bounds
+ Common::Rect subRect(subRectX, subRectY, subRectX + _workingWidth, subRectY + _workingHeight);
+
+ if (!wrap) {
+ // Clip to image bounds
+ subRect.clip(surface.w, surface.h);
+ }
+
+ // Check destRect for validity
+ if (!subRect.isValidRect() || subRect.isEmpty())
+ return;
+
+ copyRectToWorkingWindow((const uint16 *)surface.getBasePtr(subRect.left, subRect.top), destinationX, destinationY, surface.w, subRect.width(), subRect.height());
+}
+
+void RenderManager::renderImageToScreen(const Common::String &fileName, int16 destinationX, int16 destinationY, bool wrap) {
+ Graphics::Surface surface;
+ readImageToSurface(fileName, surface);
+
+ renderSubRectToScreen(surface, destinationX, destinationY, wrap);
+}
+
+void RenderManager::renderImageToScreen(Graphics::Surface &surface, int16 destinationX, int16 destinationY, bool wrap) {
+ renderSubRectToScreen(surface, destinationX, destinationY, wrap);
+}
+
+void RenderManager::readImageToSurface(const Common::String &fileName, Graphics::Surface &destination) {
+ Common::File file;
+
+ if (!file.open(fileName)) {
+ warning("Could not open file %s", fileName.c_str());
+ return;
+ }
+
+ // Read the magic number
+ // Some files are true TGA, while others are TGZ
+ uint32 fileType = file.readUint32BE();
+
+ uint32 imageWidth;
+ uint32 imageHeight;
+ Graphics::TGADecoder tga;
+ uint16 *buffer;
+ bool isTransposed = _renderTable.getRenderState() == RenderTable::PANORAMA;
+ // All ZVision images are in RGB 555
+ Graphics::PixelFormat pixelFormat555 = Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0);
+ destination.format = pixelFormat555;
+
+ bool isTGZ;
+
+ // Check for TGZ files
+ if (fileType == MKTAG('T', 'G', 'Z', '\0')) {
+ isTGZ = true;
+
+ // TGZ files have a header and then Bitmap data that is compressed with LZSS
+ uint32 decompressedSize = file.readSint32LE();
+ imageWidth = file.readSint32LE();
+ imageHeight = file.readSint32LE();
+
+ LzssReadStream lzssStream(&file);
+ buffer = (uint16 *)(new uint16[decompressedSize]);
+ lzssStream.read(buffer, decompressedSize);
+ } else {
+ isTGZ = false;
+
+ // Reset the cursor
+ file.seek(0);
+
+ // Decode
+ if (!tga.loadStream(file)) {
+ warning("Error while reading TGA image");
+ return;
+ }
+
+ Graphics::Surface tgaSurface = *(tga.getSurface());
+ imageWidth = tgaSurface.w;
+ imageHeight = tgaSurface.h;
+
+ buffer = (uint16 *)tgaSurface.getPixels();
+ }
+
+ // Flip the width and height if transposed
+ if (isTransposed) {
+ uint16 temp = imageHeight;
+ imageHeight = imageWidth;
+ imageWidth = temp;
+ }
+
+ // If the destination internal buffer is the same size as what we're copying into it,
+ // there is no need to free() and re-create
+ if (imageWidth != destination.w || imageHeight != destination.h) {
+ destination.create(imageWidth, imageHeight, pixelFormat555);
+ }
+
+ // If transposed, 'un-transpose' the data while copying it to the destination
+ // Otherwise, just do a simple copy
+ if (isTransposed) {
+ uint16 *dest = (uint16 *)destination.getPixels();
+
+ for (uint32 y = 0; y < imageHeight; ++y) {
+ uint32 columnIndex = y * imageWidth;
+
+ for (uint32 x = 0; x < imageWidth; ++x) {
+ dest[columnIndex + x] = buffer[x * imageHeight + y];
+ }
+ }
+ } else {
+ memcpy(destination.getPixels(), buffer, imageWidth * imageHeight * _pixelFormat.bytesPerPixel);
+ }
+
+ // Cleanup
+ if (isTGZ) {
+ delete[] buffer;
+ } else {
+ tga.destroy();
+ }
+
+ // Convert in place to RGB 565 from RGB 555
+ destination.convertToInPlace(_pixelFormat);
+}
+
+void RenderManager::copyRectToWorkingWindow(const uint16 *buffer, int32 destX, int32 destY, int32 imageWidth, int32 width, int32 height) {
+ uint32 destOffset = 0;
+ uint32 sourceOffset = 0;
+ uint16 *workingWindowBufferPtr = (uint16 *)_workingWindowBuffer.getBasePtr(destX, destY);
+
+ for (int32 y = 0; y < height; ++y) {
+ for (int32 x = 0; x < width; ++x) {
+ workingWindowBufferPtr[destOffset + x] = buffer[sourceOffset + x];
+ }
+
+ destOffset += _workingWidth;
+ sourceOffset += imageWidth;
+ }
+
+ if (_workingWindowDirtyRect.isEmpty()) {
+ _workingWindowDirtyRect = Common::Rect(destX, destY, destX + width, destY + height);
+ } else {
+ _workingWindowDirtyRect.extend(Common::Rect(destX, destY, destX + width, destY + height));
+ }
+
+ // TODO: Remove this from release. It's here to make sure code that uses this function clips their destinations correctly
+ assert(_workingWindowDirtyRect.width() <= _workingWidth && _workingWindowDirtyRect.height() <= _workingHeight);
+}
+
+void RenderManager::copyRectToWorkingWindow(const uint16 *buffer, int32 destX, int32 destY, int32 imageWidth, int32 width, int32 height, int16 alphaColor, uint32 idNumber) {
+ AlphaDataEntry entry;
+ entry.alphaColor = alphaColor;
+ entry.data = new Graphics::Surface();
+ entry.data->create(width, height, _pixelFormat);
+ entry.destX = destX;
+ entry.destY = destY;
+ entry.width = width;
+ entry.height = height;
+
+ uint32 sourceOffset = 0;
+ uint32 destOffset = 0;
+ uint16 *surfacePtr = (uint16 *)entry.data->getPixels();
+
+ for (int32 y = 0; y < height; ++y) {
+ for (int32 x = 0; x < width; ++x) {
+ surfacePtr[destOffset + x] = buffer[sourceOffset + x];
+ }
+
+ destOffset += width;
+ sourceOffset += imageWidth;
+ }
+
+ _alphaDataEntries[idNumber] = entry;
+}
+
+Common::Rect RenderManager::renderTextToWorkingWindow(uint32 idNumber, const Common::String &text, TruetypeFont *font, int destX, int destY, uint16 textColor, int maxWidth, int maxHeight, Graphics::TextAlign align, bool wrap) {
+ AlphaDataEntry entry;
+ entry.alphaColor = 0;
+ entry.destX = destX;
+ entry.destY = destY;
+
+ // Draw the text to the working window
+ entry.data = font->drawTextToSurface(text, textColor, maxWidth, maxHeight, align, wrap);
+ entry.width = entry.data->w;
+ entry.height = entry.data->h;
+
+ _alphaDataEntries[idNumber] = entry;
+
+ return Common::Rect(destX, destY, destX + entry.width, destY + entry.height);
+}
+
+const Common::Point RenderManager::screenSpaceToImageSpace(const Common::Point &point) {
+ // Convert from screen space to working window space
+ Common::Point newPoint(point - Common::Point(_workingWindow.left, _workingWindow.top));
+
+ RenderTable::RenderState state = _renderTable.getRenderState();
+ if (state == RenderTable::PANORAMA || state == RenderTable::TILT) {
+ newPoint = _renderTable.convertWarpedCoordToFlatCoord(newPoint);
+ }
+
+ if (state == RenderTable::PANORAMA) {
+ newPoint -= (Common::Point(_screenCenterX, 0) - _backgroundOffset);
+ } else if (state == RenderTable::TILT) {
+ newPoint -= (Common::Point(0, _screenCenterY) - _backgroundOffset);
+ }
+
+ if (newPoint.x < 0)
+ newPoint.x += _backgroundWidth;
+ else if (newPoint.x >= _backgroundWidth)
+ newPoint.x -= _backgroundWidth;
+ if (newPoint.y < 0)
+ newPoint.y += _backgroundHeight;
+ else if (newPoint.y >= _backgroundHeight)
+ newPoint.y -= _backgroundHeight;
+
+ return newPoint;
+}
+
+const Common::Point RenderManager::imageSpaceToWorkingWindowSpace(const Common::Point &point) {
+ Common::Point newPoint(point);
+
+ RenderTable::RenderState state = _renderTable.getRenderState();
+ if (state == RenderTable::PANORAMA) {
+ newPoint += (Common::Point(_screenCenterX, 0) - _backgroundOffset);
+ } else if (state == RenderTable::TILT) {
+ newPoint += (Common::Point(0, _screenCenterY) - _backgroundOffset);
+ }
+
+ return newPoint;
+}
+
+bool RenderManager::clipRectToWorkingWindow(Common::Rect &rect) {
+ if (!_workingWindow.contains(rect)) {
+ return false;
+ }
+
+ // We can't clip against the actual working window rect because it's in screen space
+ // But rect is in working window space
+ rect.clip(_workingWidth, _workingHeight);
+ return true;
+}
+
+RenderTable *RenderManager::getRenderTable() {
+ return &_renderTable;
+}
+
+void RenderManager::setBackgroundImage(const Common::String &fileName) {
+ readImageToSurface(fileName, _currentBackground);
+
+ moveBackground(0);
+}
+
+void RenderManager::setBackgroundPosition(int offset) {
+ RenderTable::RenderState state = _renderTable.getRenderState();
+ if (state == RenderTable::TILT) {
+ _backgroundOffset.x = 0;
+ _backgroundOffset.y = offset;
+ } else if (state == RenderTable::PANORAMA) {
+ _backgroundOffset.x = offset;
+ _backgroundOffset.y = 0;
+ } else {
+ _backgroundOffset.x = 0;
+ _backgroundOffset.y = 0;
+ }
+}
+
+void RenderManager::setBackgroundVelocity(int velocity) {
+ // setBackgroundVelocity(0) will be called quite often, so make sure
+ // _backgroundInverseVelocity isn't already 0 to prevent an extraneous assignment
+ if (velocity == 0) {
+ if (_backgroundInverseVelocity != 0) {
+ _backgroundInverseVelocity = 0;
+ }
+ } else {
+ _backgroundInverseVelocity = 1000 / velocity;
+ }
+}
+
+void RenderManager::moveBackground(int offset) {
+ RenderTable::RenderState state = _renderTable.getRenderState();
+ if (state == RenderTable::TILT) {
+ _backgroundOffset += Common::Point(0, offset);
+
+ _backgroundOffset.y = CLIP<int16>(_backgroundOffset.y, _screenCenterY, (int16)_backgroundHeight - _screenCenterY);
+
+ renderImageToScreen(_currentBackground, 0, _screenCenterY - _backgroundOffset.y, true);
+ } else if (state == RenderTable::PANORAMA) {
+ _backgroundOffset += Common::Point(offset, 0);
+
+ if (_backgroundOffset.x <= -_backgroundWidth)
+ _backgroundOffset.x += _backgroundWidth;
+ else if (_backgroundOffset.x >= _backgroundWidth)
+ _backgroundOffset.x -= _backgroundWidth;
+
+ renderImageToScreen(_currentBackground, _screenCenterX - _backgroundOffset.x, 0, true);
+ } else {
+ renderImageToScreen(_currentBackground, 0, 0);
+ }
+}
+
+uint32 RenderManager::getCurrentBackgroundOffset() {
+ RenderTable::RenderState state = _renderTable.getRenderState();
+
+ if (state == RenderTable::PANORAMA) {
+ return _backgroundOffset.x;
+ } else if (state == RenderTable::TILT) {
+ return _backgroundOffset.y;
+ } else {
+ return 0;
+ }
+}
+
+Graphics::Surface *RenderManager::tranposeSurface(const Graphics::Surface *surface) {
+ Graphics::Surface *tranposedSurface = new Graphics::Surface();
+ tranposedSurface->create(surface->h, surface->w, surface->format);
+
+ const uint16 *source = (const uint16 *)surface->getPixels();
+ uint16 *dest = (uint16 *)tranposedSurface->getPixels();
+
+ for (uint32 y = 0; y < tranposedSurface->h; ++y) {
+ uint32 columnIndex = y * tranposedSurface->w;
+
+ for (uint32 x = 0; x < tranposedSurface->w; ++x) {
+ dest[columnIndex + x] = source[x * surface->w + y];
+ }
+ }
+
+ return tranposedSurface;
+}
+
+} // End of namespace ZVision
diff --git a/engines/zvision/graphics/render_manager.h b/engines/zvision/graphics/render_manager.h
new file mode 100644
index 0000000000..111bf6276c
--- /dev/null
+++ b/engines/zvision/graphics/render_manager.h
@@ -0,0 +1,328 @@
+/* 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.
+ *
+ */
+
+#ifndef ZVISION_RENDER_MANAGER_H
+#define ZVISION_RENDER_MANAGER_H
+
+#include "zvision/render_table.h"
+#include "zvision/truetype_font.h"
+
+#include "common/rect.h"
+#include "common/hashmap.h"
+
+#include "graphics/surface.h"
+
+
+class OSystem;
+
+namespace Common {
+class String;
+class SeekableReadStream;
+}
+
+namespace Video {
+class VideoDecoder;
+}
+
+namespace ZVision {
+
+class RenderManager {
+public:
+ RenderManager(OSystem *system, uint32 windowWidth, uint32 windowHeight, const Common::Rect workingWindow, const Graphics::PixelFormat pixelFormat);
+ ~RenderManager();
+
+private:
+ struct AlphaDataEntry {
+ Graphics::Surface *data;
+ uint16 alphaColor;
+ uint16 destX;
+ uint16 destY;
+ uint16 width;
+ uint16 height;
+ };
+
+ typedef Common::HashMap<uint32, AlphaDataEntry> AlphaEntryMap;
+
+private:
+ OSystem *_system;
+ const Graphics::PixelFormat _pixelFormat;
+
+ // A buffer the exact same size as the workingWindow
+ // This buffer stores everything un-warped, then does a warp at the end of the frame
+ Graphics::Surface _workingWindowBuffer;
+ // A buffer representing the entire screen. Any graphical updates are first done with this buffer
+ // before actually being blitted to the screen
+ Graphics::Surface _backBuffer;
+ // A list of Alpha Entries that need to be blitted to the backbuffer
+ AlphaEntryMap _alphaDataEntries;
+
+ // A rectangle representing the portion of the working window where the pixels have been changed since last frame
+ Common::Rect _workingWindowDirtyRect;
+ // A rectangle representing the portion of the backbuffer where the pixels have been changed since last frame
+ Common::Rect _backBufferDirtyRect;
+
+ /** Width of the working window. Saved to prevent extraneous calls to _workingWindow.width() */
+ const int _workingWidth;
+ /** Height of the working window. Saved to prevent extraneous calls to _workingWindow.height() */
+ const int _workingHeight;
+ /** Center of the screen in the x direction */
+ const int _screenCenterX;
+ /** Center of the screen in the y direction */
+ const int _screenCenterY;
+
+ /**
+ * A Rectangle centered inside the actual window. All in-game coordinates
+ * are given in this coordinate space. Also, all images are clipped to the
+ * edges of this Rectangle
+ */
+ const Common::Rect _workingWindow;
+ /** Used to warp the background image */
+ RenderTable _renderTable;
+
+ Graphics::Surface _currentBackground;
+ /** The (x1,y1) coordinates of the subRectangle of the background that is currently displayed on the screen */
+ Common::Point _backgroundOffset;
+ /** The width of the current background image */
+ uint16 _backgroundWidth;
+ /** The height of the current background image */
+ uint16 _backgroundHeight;
+
+ /**
+ * The "velocity" at which the background image is panning. We actually store the inverse of velocity (ms/pixel instead of pixels/ms)
+ * because it allows you to accumulate whole pixels 'steps' instead of rounding pixels every frame
+ */
+ int _backgroundInverseVelocity;
+ /** Holds any 'leftover' milliseconds between frames */
+ uint _accumulatedVelocityMilliseconds;
+
+public:
+ void initialize();
+ /**
+ * Rotates the background image in accordance to the current _backgroundInverseVelocity
+ *
+ * @param deltaTimeInMillis The amount of time that has passed since the last frame
+ */
+ void update(uint deltaTimeInMillis);
+
+ /**
+ * Renders the current state of the backbuffer to the screen
+ */
+ void renderBackbufferToScreen();
+
+ /**
+ * Renders all AlphaEntries to the backbuffer
+ */
+ void processAlphaEntries();
+ /**
+ * Clears the AlphaEntry list
+ */
+ void clearAlphaEntries() { _alphaDataEntries.clear(); }
+ /**
+ * Removes a specific AlphaEntry from the list
+ *
+ * @param idNumber The id number identifing the AlphaEntry
+ */
+ void removeAlphaEntry(uint32 idNumber) { _alphaDataEntries.erase(idNumber); }
+
+ /**
+ * Copies a sub-rectangle of a buffer to the working window
+ *
+ * @param buffer The pixel data to copy to the working window
+ * @param destX The X destination in the working window where the subRect of data should be put
+ * @param destY The Y destination in the working window where the subRect of data should be put
+ * @param imageWidth The width of the source image
+ * @param width The width of the sub rectangle
+ * @param height The height of the sub rectangle
+ */
+ void copyRectToWorkingWindow(const uint16 *buffer, int32 destX, int32 destY, int32 imageWidth, int32 width, int32 height);
+ /**
+ * Copies a sub-rectangle of a buffer to the working window with binary alpha support.
+ *
+ * @param buffer The pixel data to copy to the working window
+ * @param destX The X destination in the working window where the subRect of data should be put
+ * @param destY The Y destination in the working window where the subRect of data should be put
+ * @param imageWidth The width of the source image
+ * @param width The width of the sub rectangle
+ * @param height The height of the sub rectangle
+ * @param alphaColor The color to interpret as meaning 'transparent'
+ * @param idNumber A unique identifier for the data being copied over.
+ */
+ void copyRectToWorkingWindow(const uint16 *buffer, int32 destX, int32 destY, int32 imageWidth, int32 width, int32 height, int16 alphaColor, uint32 idNumber);
+
+ /**
+ * Renders the supplied text to the working window
+ *
+ * @param idNumber A unique identifier for the text
+ * @param text The text to be rendered
+ * @param font The font to use to render the text
+ * @param destX The X destination in the working window where the text should be rendered
+ * @param destY The Y destination in the working window where the text should be rendered
+ * @param textColor The color to render the text with (in RBG 565)
+ * @param maxWidth The max width the text should take up.
+ * @param maxHeight The max height the text should take up.
+ * @param align The alignment of the text within the bounds of maxWidth
+ * @param wrap If true, any words extending past maxWidth will wrap to a new line. If false, ellipses will be rendered to show that the text didn't fit
+ * @return A rectangle representing where the text was drawn in the working window
+ */
+ Common::Rect renderTextToWorkingWindow(uint32 idNumber, const Common::String &text, TruetypeFont *font, int destX, int destY, uint16 textColor, int maxWidth, int maxHeight = -1, Graphics::TextAlign align = Graphics::kTextAlignLeft, bool wrap = true);
+
+ /**
+ * Fills the entire workingWindow with the specified color. Internally, the color
+ * will be converted to RGB 565 and then blitted.
+ *
+ * @param color The color to fill the working window with. (In RGB 555)
+ */
+ void clearWorkingWindowTo555Color(uint16 color);
+
+ /**
+ * Blits the image or a portion of the image to the backbuffer. Actual screen updates won't happen until the end of the frame.
+ * The image will be clipped to fit inside the working window. Coords are in working window space, not screen space!
+ *
+ * @param fileName Name of the image file
+ * @param destinationX X position where the image should be put. Coords are in working window space, not screen space!
+ * @param destinationY Y position where the image should be put. Coords are in working window space, not screen space!
+ */
+ void renderImageToScreen(const Common::String &fileName, int16 destinationX, int16 destinationY, bool wrap = false);
+
+ /**
+ * Blits the image or a portion of the image to the backbuffer. Actual screen updates won't happen until the end of the frame.
+ * The image will be clipped to fit inside the working window. Coords are in working window space, not screen space!
+ *
+ * @param stream Surface to read the image data from
+ * @param destinationX X position where the image should be put. Coords are in working window space, not screen space!
+ * @param destinationY Y position where the image should be put. Coords are in working window space, not screen space!
+ */
+ void renderImageToScreen(Graphics::Surface &surface, int16 destinationX, int16 destinationY, bool wrap = false);
+
+ /**
+ * Sets the current background image to be used by the RenderManager and immediately
+ * blits it to the screen. (It won't show up until the end of the frame)
+ *
+ * @param fileName The name of the image file
+ */
+ void setBackgroundImage(const Common::String &fileName);
+
+ /**
+ * Set the background position (_backgroundOffset). If the current RenderState is PANORAMA, the offset
+ * will be in the horizontal direction. If the current RenderState is TILT, the offset will be in the
+ * vertical direction.
+ *
+ * This method will not render anything on the screen. So if nothing else is called that renders the
+ * background, the change won't be seen until next frame.
+ *
+ * @param offset The amount to offset the background
+ */
+ void setBackgroundPosition(int offset);
+
+ /**
+ * Set the background scroll velocity. Negative velocities correspond to left / up scrolling and
+ * positive velocities correspond to right / down scrolling
+ *
+ * @param velocity Velocity
+ */
+ void setBackgroundVelocity(int velocity);
+
+ /**
+ * Converts a point in screen coordinate space to image coordinate space
+ *
+ * @param point Point in screen coordinate space
+ * @return Point in image coordinate space
+ */
+ const Common::Point screenSpaceToImageSpace(const Common::Point &point);
+ /**
+ * Converts a point in image coordinate space to ***PRE-WARP***
+ * working window coordinate space
+ *
+ * @param point Point in image coordinate space
+ * @return Point in PRE-WARP working window coordinate space
+ */
+ const Common::Point imageSpaceToWorkingWindowSpace(const Common::Point &point);
+
+ /**
+ * Clip a rectangle to the working window. If it returns false, the original rect
+ * is not inside the working window.
+ *
+ * @param rect The rectangle to clip against the working window
+ * @return Is rect at least partially inside the working window (true) or completely outside (false)
+ */
+ bool clipRectToWorkingWindow(Common::Rect &rect);
+
+ RenderTable *getRenderTable();
+ uint32 getCurrentBackgroundOffset();
+ const Graphics::Surface *getBackBuffer() { return &_backBuffer; }
+
+ /**
+ * Creates a copy of surface and transposes the data.
+ *
+ * Note: The user is responsible for calling free() on the returned surface
+ * and then deleting it
+ *
+ * @param surface The data to be transposed
+ * @return A copy of the surface with the data transposed
+ */
+ static Graphics::Surface *tranposeSurface(const Graphics::Surface *surface);
+
+private:
+ /**
+ * Renders a subRectangle of an image to the backbuffer. The destinationRect and SubRect
+ * will be clipped to image bound and to working window bounds
+ *
+ * @param buffer Pointer to (0, 0) of the image data
+ * @param imageWidth The width of the original image (not of the subRectangle)
+ * @param imageHeight The width of the original image (not of the subRectangle)
+ * @param horizontalPitch The horizontal pitch of the original image
+ * @param destinationX The x coordinate (in working window space) of where to put the final image
+ * @param destinationY The y coordinate (in working window space) of where to put the final image
+ * @param subRectangle A rectangle representing the part of the image that should be rendered
+ * @param wrap Should the image wrap (tile) if it doesn't completely fill the screen?
+ */
+ void renderSubRectToScreen(Graphics::Surface &surface, int16 destinationX, int16 destinationY, bool wrap);
+
+ /**
+ * Reads an image file pixel data into a Surface buffer. In the process
+ * it converts the pixel data from RGB 555 to RGB 565. Also, if the image
+ * is transposed, it will un-transpose the pixel data. The function will
+ * call destination::create() if the dimensions of destination do not match
+ * up with the dimensions of the image.
+ *
+ * @param fileName The name of a .tga file
+ * @param destination A reference to the Surface to store the pixel data in
+ */
+ void readImageToSurface(const Common::String &fileName, Graphics::Surface &destination);
+
+ /**
+ * Move the background image by an offset. If we are currently in Panorama mode,
+ * the offset will correspond to a horizontal motion. If we are currently in Tilt mode,
+ * the offset will correspond to a vertical motion. This function should not be called
+ * if we are in Flat mode.
+ *
+ * The RenderManager will take care of wrapping the image.
+ * Ex: If the image has width 1400px, it is legal to offset 1500px.
+ *
+ * @param offset The amount to move the background
+ */
+ void moveBackground(int offset);
+};
+
+} // End of namespace ZVision
+
+#endif
diff --git a/engines/zvision/graphics/render_table.cpp b/engines/zvision/graphics/render_table.cpp
new file mode 100644
index 0000000000..b6a6a3d2bb
--- /dev/null
+++ b/engines/zvision/graphics/render_table.cpp
@@ -0,0 +1,240 @@
+/* ScummVM - Graphic Adventure Engine
+*
+* ScummVM is the legal property of its developers, whose names
+* are too numerous to list here. Please refer to the COPYRIGHT
+* file distributed with this source distribution.
+*
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License
+* as published by the Free Software Foundation; either version 2
+* of the License, or (at your option) any later version.
+
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*
+*/
+
+#include "common/scummsys.h"
+
+#include "zvision/render_table.h"
+
+#include "common/rect.h"
+
+#include "graphics/colormasks.h"
+
+
+namespace ZVision {
+
+RenderTable::RenderTable(uint numColumns, uint numRows)
+ : _numRows(numRows),
+ _numColumns(numColumns),
+ _renderState(FLAT) {
+ assert(numRows != 0 && numColumns != 0);
+
+ _internalBuffer = new Common::Point[numRows * numColumns];
+}
+
+RenderTable::~RenderTable() {
+ delete[] _internalBuffer;
+}
+
+void RenderTable::setRenderState(RenderState newState) {
+ _renderState = newState;
+
+ switch (newState) {
+ case PANORAMA:
+ _panoramaOptions.fieldOfView = 27.0f;
+ _panoramaOptions.linearScale = 0.55f;
+ _panoramaOptions.reverse = false;
+ break;
+ case TILT:
+ _tiltOptions.fieldOfView = 27.0f;
+ _tiltOptions.linearScale = 0.55f;
+ _tiltOptions.reverse = false;
+ break;
+ case FLAT:
+ // Intentionally left empty
+ break;
+ }
+}
+
+const Common::Point RenderTable::convertWarpedCoordToFlatCoord(const Common::Point &point) {
+ // If we're outside the range of the RenderTable, no warping is happening. Return the maximum image coords
+ if (point.x >= (int16)_numColumns || point.y >= (int16)_numRows || point.x < 0 || point.y < 0) {
+ int16 x = CLIP<int16>(point.x, 0, (int16)_numColumns);
+ int16 y = CLIP<int16>(point.y, 0, (int16)_numRows);
+ return Common::Point(x, y);
+ }
+
+ uint32 index = point.y * _numColumns + point.x;
+
+ Common::Point newPoint(point);
+ newPoint.x += _internalBuffer[index].x;
+ newPoint.y += _internalBuffer[index].y;
+
+ return newPoint;
+}
+
+uint16 mixTwoRGB(uint16 colorOne, uint16 colorTwo, float percentColorOne) {
+ assert(percentColorOne < 1.0f);
+
+ float rOne = float((colorOne & Graphics::ColorMasks<555>::kRedMask) >> Graphics::ColorMasks<555>::kRedShift);
+ float rTwo = float((colorTwo & Graphics::ColorMasks<555>::kRedMask) >> Graphics::ColorMasks<555>::kRedShift);
+ float gOne = float((colorOne & Graphics::ColorMasks<555>::kGreenMask) >> Graphics::ColorMasks<555>::kGreenShift);
+ float gTwo = float((colorTwo & Graphics::ColorMasks<555>::kGreenMask) >> Graphics::ColorMasks<555>::kGreenShift);
+ float bOne = float((colorOne & Graphics::ColorMasks<555>::kBlueMask) >> Graphics::ColorMasks<555>::kBlueShift);
+ float bTwo = float((colorTwo & Graphics::ColorMasks<555>::kBlueMask) >> Graphics::ColorMasks<555>::kBlueShift);
+
+ float rFinal = rOne * percentColorOne + rTwo * (1.0f - percentColorOne);
+ float gFinal = gOne * percentColorOne + gTwo * (1.0f - percentColorOne);
+ float bFinal = bOne * percentColorOne + bTwo * (1.0f - percentColorOne);
+
+ uint16 returnColor = (byte(rFinal + 0.5f) << Graphics::ColorMasks<555>::kRedShift) |
+ (byte(gFinal + 0.5f) << Graphics::ColorMasks<555>::kGreenShift) |
+ (byte(bFinal + 0.5f) << Graphics::ColorMasks<555>::kBlueShift);
+
+ return returnColor;
+}
+
+void RenderTable::mutateImage(uint16 *sourceBuffer, uint16* destBuffer, uint32 destWidth, const Common::Rect &subRect) {
+ uint32 destOffset = 0;
+
+ for (int16 y = subRect.top; y < subRect.bottom; ++y) {
+ uint32 sourceOffset = y * _numColumns;
+
+ for (int16 x = subRect.left; x < subRect.right; ++x) {
+ uint32 normalizedX = x - subRect.left;
+ uint32 index = sourceOffset + x;
+
+ // RenderTable only stores offsets from the original coordinates
+ uint32 sourceYIndex = y + _internalBuffer[index].y;
+ uint32 sourceXIndex = x + _internalBuffer[index].x;
+
+ destBuffer[destOffset + normalizedX] = sourceBuffer[sourceYIndex * _numColumns + sourceXIndex];
+ }
+
+ destOffset += destWidth;
+ }
+}
+
+void RenderTable::generateRenderTable() {
+ switch (_renderState) {
+ case ZVision::RenderTable::PANORAMA:
+ generatePanoramaLookupTable();
+ break;
+ case ZVision::RenderTable::TILT:
+ generateTiltLookupTable();
+ break;
+ case ZVision::RenderTable::FLAT:
+ // Intentionally left empty
+ break;
+ }
+}
+
+void RenderTable::generatePanoramaLookupTable() {
+ memset(_internalBuffer, 0, _numRows * _numColumns * sizeof(uint16));
+
+ float halfWidth = (float)_numColumns / 2.0f;
+ float halfHeight = (float)_numRows / 2.0f;
+
+ float fovInRadians = (_panoramaOptions.fieldOfView * M_PI / 180.0f);
+ float cylinderRadius = halfHeight / tan(fovInRadians);
+
+ for (uint x = 0; x < _numColumns; ++x) {
+ // Add an offset of 0.01 to overcome zero tan/atan issue (vertical line on half of screen)
+ // Alpha represents the horizontal angle between the viewer at the center of a cylinder and x
+ float alpha = atan(((float)x - halfWidth + 0.01f) / cylinderRadius);
+
+ // To get x in cylinder coordinates, we just need to calculate the arc length
+ // We also scale it by _panoramaOptions.linearScale
+ int32 xInCylinderCoords = int32(floor((cylinderRadius * _panoramaOptions.linearScale * alpha) + halfWidth));
+
+ float cosAlpha = cos(alpha);
+
+ for (uint y = 0; y < _numRows; ++y) {
+ // To calculate y in cylinder coordinates, we can do similar triangles comparison,
+ // comparing the triangle from the center to the screen and from the center to the edge of the cylinder
+ int32 yInCylinderCoords = int32(floor(halfHeight + ((float)y - halfHeight) * cosAlpha));
+
+ uint32 index = y * _numColumns + x;
+
+ // Only store the (x,y) offsets instead of the absolute positions
+ _internalBuffer[index].x = xInCylinderCoords - x;
+ _internalBuffer[index].y = yInCylinderCoords - y;
+ }
+ }
+}
+
+void RenderTable::generateTiltLookupTable() {
+ float halfWidth = (float)_numColumns / 2.0f;
+ float halfHeight = (float)_numRows / 2.0f;
+
+ float fovInRadians = (_tiltOptions.fieldOfView * M_PI / 180.0f);
+ float cylinderRadius = halfWidth / tan(fovInRadians);
+
+ for (uint y = 0; y < _numRows; ++y) {
+
+ // Add an offset of 0.01 to overcome zero tan/atan issue (horizontal line on half of screen)
+ // Alpha represents the vertical angle between the viewer at the center of a cylinder and y
+ float alpha = atan(((float)y - halfHeight + 0.01f) / cylinderRadius);
+
+ // To get y in cylinder coordinates, we just need to calculate the arc length
+ // We also scale it by _tiltOptions.linearScale
+ int32 yInCylinderCoords = int32(floor((cylinderRadius * _tiltOptions.linearScale * alpha) + halfHeight));
+
+ float cosAlpha = cos(alpha);
+ uint32 columnIndex = y * _numColumns;
+
+ for (uint x = 0; x < _numColumns; ++x) {
+ // To calculate x in cylinder coordinates, we can do similar triangles comparison,
+ // comparing the triangle from the center to the screen and from the center to the edge of the cylinder
+ int32 xInCylinderCoords = int32(floor(halfWidth + ((float)x - halfWidth) * cosAlpha));
+
+ uint32 index = columnIndex + x;
+
+ // Only store the (x,y) offsets instead of the absolute positions
+ _internalBuffer[index].x = xInCylinderCoords - x;
+ _internalBuffer[index].y = yInCylinderCoords - y;
+ }
+ }
+}
+
+void RenderTable::setPanoramaFoV(float fov) {
+ assert(fov > 0.0f);
+
+ _panoramaOptions.fieldOfView = fov;
+}
+
+void RenderTable::setPanoramaScale(float scale) {
+ assert(scale > 0.0f);
+
+ _panoramaOptions.linearScale = scale;
+}
+
+void RenderTable::setPanoramaReverse(bool reverse) {
+ _panoramaOptions.reverse = reverse;
+}
+
+void RenderTable::setTiltFoV(float fov) {
+ assert(fov > 0.0f);
+
+ _tiltOptions.fieldOfView = fov;
+}
+
+void RenderTable::setTiltScale(float scale) {
+ assert(scale > 0.0f);
+
+ _tiltOptions.linearScale = scale;
+}
+
+void RenderTable::setTiltReverse(bool reverse) {
+ _tiltOptions.reverse = reverse;
+}
+
+} // End of namespace ZVision
diff --git a/engines/zvision/graphics/render_table.h b/engines/zvision/graphics/render_table.h
new file mode 100644
index 0000000000..898091193a
--- /dev/null
+++ b/engines/zvision/graphics/render_table.h
@@ -0,0 +1,85 @@
+/* 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.
+ *
+ */
+
+#ifndef ZVISION_RENDER_TABLE_H
+#define ZVISION_RENDER_TABLE_H
+
+#include "common/rect.h"
+
+
+namespace ZVision {
+
+class RenderTable {
+public:
+ RenderTable(uint numRows, uint numColumns);
+ ~RenderTable();
+
+public:
+ enum RenderState {
+ PANORAMA,
+ TILT,
+ FLAT
+ };
+
+private:
+ uint _numColumns, _numRows;
+ Common::Point *_internalBuffer;
+ RenderState _renderState;
+
+ struct {
+ float fieldOfView;
+ float linearScale;
+ bool reverse;
+ } _panoramaOptions;
+
+ // TODO: See if tilt and panorama need to have separate options
+ struct {
+ float fieldOfView;
+ float linearScale;
+ bool reverse;
+ } _tiltOptions;
+
+public:
+ RenderState getRenderState() { return _renderState; }
+ void setRenderState(RenderState newState);
+
+ const Common::Point convertWarpedCoordToFlatCoord(const Common::Point &point);
+
+ void mutateImage(uint16 *sourceBuffer, uint16* destBuffer, uint32 destWidth, const Common::Rect &subRect);
+ void generateRenderTable();
+
+ void setPanoramaFoV(float fov);
+ void setPanoramaScale(float scale);
+ void setPanoramaReverse(bool reverse);
+
+ void setTiltFoV(float fov);
+ void setTiltScale(float scale);
+ void setTiltReverse(bool reverse);
+
+private:
+ void generatePanoramaLookupTable();
+ void generateTiltLookupTable();
+};
+
+} // End of namespace ZVision
+
+#endif