aboutsummaryrefslogtreecommitdiff
path: root/engines/titanic/support/video_surface.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/titanic/support/video_surface.cpp')
-rw-r--r--engines/titanic/support/video_surface.cpp663
1 files changed, 663 insertions, 0 deletions
diff --git a/engines/titanic/support/video_surface.cpp b/engines/titanic/support/video_surface.cpp
new file mode 100644
index 0000000000..e24063188d
--- /dev/null
+++ b/engines/titanic/support/video_surface.cpp
@@ -0,0 +1,663 @@
+/* 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 "titanic/support/video_surface.h"
+#include "titanic/support/image_decoders.h"
+#include "titanic/support/screen_manager.h"
+#include "titanic/support/transparency_surface.h"
+#include "titanic/titanic.h"
+
+namespace Titanic {
+
+int CVideoSurface::_videoSurfaceCounter = 0;
+byte CVideoSurface::_palette1[32][32];
+byte CVideoSurface::_palette2[32][32];
+
+CVideoSurface::CVideoSurface(CScreenManager *screenManager) :
+ _screenManager(screenManager), _rawSurface(nullptr), _movie(nullptr),
+ _pendingLoad(false), _flipVertically(false), _fastBlitFlag(false),
+ _transparencySurface(nullptr), _transparencyMode(TRANS_DEFAULT),
+ _freeTransparencySurface(DisposeAfterUse::NO), _hasFrame(true), _lockCount(0) {
+ _videoSurfaceNum = _videoSurfaceCounter++;
+}
+
+CVideoSurface::~CVideoSurface() {
+ --_videoSurfaceCounter;
+
+ if (_freeTransparencySurface == DisposeAfterUse::YES)
+ delete _transparencySurface;
+}
+
+void CVideoSurface::setupPalette(byte palette[32][32], byte val) {
+ for (uint idx1 = 0; idx1 < 32; ++idx1) {
+ for (uint idx2 = 0, base = 0; idx2 < 32; ++idx2, base += idx1) {
+ uint v = base / 31;
+ palette[idx1][idx2] = (byte)v;
+
+ if (val != 0xff && v != idx2) {
+ assert(0);
+ }
+ }
+ }
+}
+
+void CVideoSurface::setSurface(CScreenManager *screenManager, DirectDrawSurface *surface) {
+ _screenManager = screenManager;
+ _ddSurface = surface;
+}
+
+void CVideoSurface::blitFrom(const Point &destPos, CVideoSurface *src, const Rect *srcRect) {
+ if (loadIfReady() && src->loadIfReady() && _ddSurface && src->_ddSurface) {
+ Rect srcBounds, destBounds;
+ clipBounds(srcBounds, destBounds, src, srcRect, &destPos);
+
+ if (src->_flipVertically)
+ flippedBlitRect(srcBounds, destBounds, src);
+ else
+ blitRect(srcBounds, destBounds, src);
+ }
+}
+
+void CVideoSurface::blitFrom(const Point &destPos, const Graphics::Surface *src) {
+ lock();
+ _rawSurface->blitFrom(*src, destPos);
+ unlock();
+}
+
+void CVideoSurface::clipBounds(Rect &srcRect, Rect &destRect,
+ CVideoSurface *srcSurface, const Rect *subRect, const Point *destPos) {
+ // Figure out initial source rect and dest rect, based on whether
+ // specific subRect and/or destPos have been passed
+ if (destPos) {
+ destRect.left = destPos->x;
+ destRect.top = destPos->y;
+ } else {
+ destRect.left = destRect.top = 0;
+ }
+
+ if (subRect) {
+ destRect.right = destRect.left + subRect->width();
+ destRect.bottom = destRect.top + subRect->height();
+ srcRect = *subRect;
+ } else {
+ srcRect.right = srcRect.left + srcSurface->getWidth();
+ srcRect.bottom = srcRect.top + srcSurface->getHeight();
+ srcRect = Rect(0, 0, srcSurface->getWidth(), srcSurface->getHeight());
+ }
+
+ // Clip destination rect to be on-screen
+ if (destRect.left < 0) {
+ srcRect.left -= destRect.left;
+ destRect.left = 0;
+ }
+ if (destRect.top < 0) {
+ srcRect.top -= destRect.top;
+ destRect.top = 0;
+ }
+ if (destRect.right > getWidth()) {
+ srcRect.right += getWidth() - destRect.right;
+ destRect.right = getWidth();
+ }
+ if (destRect.bottom > getHeight()) {
+ srcRect.bottom += getHeight() - destRect.bottom;
+ destRect.bottom = getHeight();
+ }
+
+ // Clip source rect to be within the source surface
+ if (srcRect.left < 0) {
+ destRect.left -= srcRect.left;
+ srcRect.left = 0;
+ }
+ if (srcRect.top < 0) {
+ destRect.top -= srcRect.top;
+ srcRect.top = 0;
+ }
+ if (srcRect.right > srcSurface->getWidth()) {
+ destRect.right += srcSurface->getWidth() - srcRect.right;
+ srcRect.right = srcSurface->getWidth();
+ }
+ if (srcRect.bottom > srcSurface->getHeight()) {
+ destRect.bottom += srcSurface->getHeight() - srcRect.bottom;
+ srcRect.bottom = srcSurface->getHeight();
+ }
+
+ // Validate that the resulting rects are valid
+ if (destRect.left >= destRect.right || destRect.top >= destRect.bottom
+ || srcRect.left >= srcRect.right || srcRect.top >= srcRect.bottom)
+ error("Invalid rect");
+}
+
+void CVideoSurface::blitRect(const Rect &srcRect, const Rect &destRect, CVideoSurface *src) {
+ src->lock();
+ lock();
+
+ if (src->_fastBlitFlag) {
+ _rawSurface->blitFrom(*src->_rawSurface, srcRect, Point(destRect.left, destRect.top));
+ } else if (src->getTransparencySurface()) {
+ transBlitRect(srcRect, destRect, src, false);
+ } else if (lock()) {
+ if (src->lock()) {
+ const Graphics::ManagedSurface *srcSurface = src->_rawSurface;
+ Graphics::ManagedSurface *destSurface = _rawSurface;
+ const uint transColor = src->getTransparencyColor();
+
+ destSurface->transBlitFrom(*srcSurface, srcRect, destRect, transColor);
+
+ src->unlock();
+ }
+
+ unlock();
+ }
+}
+
+void CVideoSurface::flippedBlitRect(const Rect &srcRect, const Rect &destRect, CVideoSurface *src) {
+ if (src->getTransparencySurface()) {
+ transBlitRect(srcRect, destRect, src, true);
+ } else if (lock()) {
+ if (src->lock()) {
+ Graphics::ManagedSurface *srcSurface = src->_rawSurface;
+ Graphics::ManagedSurface *destSurface = _rawSurface;
+ const Graphics::Surface srcArea = srcSurface->getSubArea(srcRect);
+ const uint transColor = src->getTransparencyColor();
+
+ // Vertically flip the source area
+ Graphics::ManagedSurface flippedArea(srcArea.w, srcArea.h, srcArea.format);
+ for (int y = 0; y < srcArea.h; ++y) {
+ const byte *pSrc = (const byte *)srcArea.getBasePtr(0, y);
+ byte *pDest = (byte *)flippedArea.getBasePtr(0, flippedArea.h - y - 1);
+ Common::copy(pSrc, pSrc + srcArea.pitch, pDest);
+ }
+
+ destSurface->transBlitFrom(flippedArea,
+ Common::Point(destRect.left, destRect.top), transColor);
+
+ src->unlock();
+ }
+
+ unlock();
+ }
+}
+
+void CVideoSurface::transBlitRect(const Rect &srcRect, const Rect &destRect, CVideoSurface *src, bool flipFlag) {
+ assert(srcRect.width() == destRect.width() && srcRect.height() == destRect.height());
+ assert(src->getPixelDepth() == 2);
+
+ if (lock()) {
+ if (src->lock()) {
+ Graphics::ManagedSurface *srcSurface = src->_rawSurface;
+ Graphics::ManagedSurface *destSurface = _rawSurface;
+ Graphics::Surface destArea = destSurface->getSubArea(destRect);
+
+ const uint16 *srcPtr = (const uint16 *)srcSurface->getBasePtr(
+ srcRect.left, flipFlag ? srcRect.top : srcRect.bottom - 1);
+ uint16 *destPtr = (uint16 *)destArea.getBasePtr(0, destArea.h - 1);
+ bool isAlpha = src->_transparencyMode == TRANS_ALPHA0 ||
+ src->_transparencyMode == TRANS_ALPHA255;
+
+ CTransparencySurface transSurface(src->getTransparencySurface(), src->_transparencyMode);
+
+ for (int yCtr = 0; yCtr < srcRect.height(); ++yCtr) {
+ // Prepare for copying the line
+ const uint16 *lineSrcP = srcPtr;
+ uint16 *lineDestP = destPtr;
+ transSurface.setRow(flipFlag ? srcRect.top + yCtr : srcRect.bottom - yCtr - 1);
+ transSurface.setCol(srcRect.left);
+
+ for (int srcX = srcRect.left; srcX < srcRect.right; ++srcX) {
+ copyPixel(lineDestP, lineSrcP, transSurface.getAlpha() >> 3, srcSurface->format, isAlpha);
+
+ ++lineSrcP;
+ ++lineDestP;
+ transSurface.moveX();
+ }
+
+ // Move to next line
+ srcPtr = flipFlag ? srcPtr + (src->getPitch() / 2) :
+ srcPtr - (src->getPitch() / 2);
+ destPtr -= destArea.pitch / 2;
+ }
+
+ src->unlock();
+ }
+
+ unlock();
+ }
+}
+
+uint CVideoSurface::getTransparencyColor() {
+ return getPixelDepth() == 2 ? 0xf81f : 0x7c1f;
+}
+
+bool CVideoSurface::hasFrame() {
+ if (_hasFrame) {
+ _hasFrame = false;
+ return true;
+ } else if (_movie) {
+ return _movie->hasVideoFrame();
+ } else {
+ return false;
+ }
+}
+
+#define RGB_SHIFT 3
+void CVideoSurface::copyPixel(uint16 *destP, const uint16 *srcP, byte alpha,
+ const Graphics::PixelFormat &srcFormat, bool isAlpha) {
+ const Graphics::PixelFormat destFormat = _ddSurface->getFormat();
+ alpha &= 0xff;
+ assert(alpha < 32);
+
+ // Get the source color
+ byte r, g, b;
+ srcFormat.colorToRGB(*srcP, r, g, b);
+ r >>= RGB_SHIFT;
+ g >>= RGB_SHIFT;
+ b >>= RGB_SHIFT;
+
+ if (isAlpha) {
+ r = _palette1[31 - alpha][r];
+ g = _palette1[31 - alpha][g];
+ b = _palette1[31 - alpha][b];
+ }
+
+ byte r2, g2, b2;
+ destFormat.colorToRGB(*destP, r2, g2, b2);
+ r2 >>= RGB_SHIFT;
+ g2 >>= RGB_SHIFT;
+ b2 >>= RGB_SHIFT;
+ r2 = _palette1[alpha][r2];
+ g2 = _palette1[alpha][g2];
+ b2 = _palette1[alpha][b2];
+
+ *destP = destFormat.RGBToColor((r + r2) << RGB_SHIFT,
+ (g + g2) << RGB_SHIFT, (b + b2) << RGB_SHIFT);
+}
+
+/*------------------------------------------------------------------------*/
+
+OSVideoSurface::OSVideoSurface(CScreenManager *screenManager, DirectDrawSurface *surface) :
+ CVideoSurface(screenManager) {
+ _ddSurface = surface;
+}
+
+OSVideoSurface::OSVideoSurface(CScreenManager *screenManager, const CResourceKey &key, bool pendingLoad) :
+ CVideoSurface(screenManager) {
+ _ddSurface = nullptr;
+ _pendingLoad = pendingLoad;
+
+ if (_pendingLoad) {
+ loadResource(key);
+ } else {
+ _resourceKey = key;
+ load();
+ }
+}
+
+OSVideoSurface::~OSVideoSurface() {
+ if (_ddSurface)
+ _videoSurfaceCounter -= OSVideoSurface::freeSurface();
+}
+
+void OSVideoSurface::loadResource(const CResourceKey &key) {
+ _resourceKey = key;
+ _pendingLoad = true;
+
+ if (hasSurface())
+ load();
+}
+
+void OSVideoSurface::loadTarga(const CResourceKey &key) {
+ // Decode the image
+ CTargaDecode decoder;
+ decoder.decode(*this, key.getString());
+
+ if (getPixelDepth() == 2)
+ shiftColors();
+
+ _resourceKey = key;
+
+}
+
+void OSVideoSurface::loadJPEG(const CResourceKey &key) {
+ // Decode the image
+ CJPEGDecode decoder;
+ decoder.decode(*this, key.getString());
+
+ if (getPixelDepth() == 2)
+ shiftColors();
+
+ _resourceKey = key;
+}
+
+void OSVideoSurface::loadTarga(const CString &name) {
+ CResourceKey key(name);
+ loadTarga(key);
+}
+
+void OSVideoSurface::loadMovie(const CResourceKey &key, bool destroyFlag) {
+ // Delete any prior movie
+ if (_movie) {
+ delete _movie;
+ _movie = nullptr;
+ }
+
+ // Create the new movie and load the first frame to the video surface
+ _movie = g_vm->_movieManager.createMovie(key, this);
+ _movie->setFrame(0);
+
+ // If flagged to destroy, then immediately destroy movie instance
+ if (destroyFlag) {
+ delete _movie;
+ _movie = nullptr;
+ }
+
+ _resourceKey = key;
+}
+
+bool OSVideoSurface::lock() {
+ if (!loadIfReady())
+ return false;
+
+ ++_lockCount;
+ _rawSurface = _ddSurface->lock(nullptr, 0);
+ return true;
+}
+
+void OSVideoSurface::unlock() {
+ if (!--_lockCount) {
+ if (_rawSurface)
+ _ddSurface->unlock();
+ _rawSurface = nullptr;
+ }
+}
+
+bool OSVideoSurface::hasSurface() {
+ return _ddSurface != nullptr;
+}
+
+int OSVideoSurface::getWidth() {
+ if (!loadIfReady())
+ error("Could not load resource");
+
+ return _ddSurface->getWidth();
+}
+
+int OSVideoSurface::getHeight() {
+ if (!loadIfReady())
+ error("Could not load resource");
+
+ return _ddSurface->getHeight();
+}
+
+int OSVideoSurface::getPitch() {
+ if (!loadIfReady())
+ error("Could not load resource");
+
+ return _ddSurface->getPitch();
+}
+
+int OSVideoSurface::getBpp() {
+ if (!loadIfReady())
+ error("Could not load resource");
+
+ return getPixelDepth();
+}
+
+void OSVideoSurface::recreate(int width, int height, int bpp) {
+ freeSurface();
+
+ _screenManager->resizeSurface(this, width, height, bpp);
+ if (_ddSurface)
+ _videoSurfaceCounter += _ddSurface->getSize();
+}
+
+void OSVideoSurface::resize(int width, int height, int bpp) {
+ if (!_ddSurface || _ddSurface->getWidth() != width ||
+ _ddSurface->getHeight() != height)
+ recreate(width, height, bpp);
+}
+
+void OSVideoSurface::detachSurface() {
+ _ddSurface = nullptr;
+}
+
+int OSVideoSurface::getPixelDepth() {
+ if (!loadIfReady())
+ error("Could not load resource");
+
+ lock();
+
+ int result = _rawSurface->format.bytesPerPixel;
+ if (result == 1)
+ // Paletted 8-bit images don't store the color directly in the pixels
+ result = 0;
+
+ unlock();
+ return result;
+}
+
+bool OSVideoSurface::load() {
+ if (!_resourceKey.scanForFile())
+ return false;
+
+ switch (_resourceKey.fileTypeSuffix()) {
+ case FILETYPE_IMAGE:
+ switch (_resourceKey.imageTypeSuffix()) {
+ case IMAGETYPE_TARGA:
+ loadTarga(_resourceKey);
+ break;
+ case IMAGETYPE_JPEG:
+ loadJPEG(_resourceKey);
+ break;
+ default:
+ break;
+ }
+ return true;
+
+ case FILETYPE_MOVIE:
+ loadMovie(_resourceKey);
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+uint16 OSVideoSurface::getPixel(const Common::Point &pt) {
+ if (!loadIfReady())
+ return 0;
+
+ if (pt.x >= 0 && pt.y >= 0 && pt.x < getWidth() && pt.y < getHeight()) {
+ if (_transparencySurface) {
+ // WORKAROUND: Original had the setRow _flipVertically check in reverse.
+ // Pretty sure putting it the way is below is the correct way
+ CTransparencySurface transSurface(&_transparencySurface->rawSurface(), _transparencyMode);
+ transSurface.setRow(_flipVertically ? getHeight() - pt.y - 1 : pt.y);
+ transSurface.setCol(pt.x);
+
+ if (transSurface.isPixelTransparent())
+ return getTransparencyColor();
+ }
+
+ lock();
+ uint16 pixel = *(uint16 *)_rawSurface->getBasePtr(pt.x, pt.y);
+ unlock();
+ return pixel;
+ } else {
+ return getTransparencyColor();
+ }
+}
+
+void OSVideoSurface::setPixel(const Point &pt, uint pixel) {
+ assert(getPixelDepth() == 2);
+
+ uint16 *pixelP = (uint16 *)_rawSurface->getBasePtr(pt.x, pt.y);
+ *pixelP = pixel;
+}
+
+void OSVideoSurface::shiftColors() {
+ if (!loadIfReady())
+ return;
+
+ // Currently no further processing is needed, since for ScummVM,
+ // we already convert 16-bit surfaces as soon as they're loaded
+}
+
+void OSVideoSurface::clear() {
+ if (!loadIfReady())
+ error("Could not load resource");
+
+}
+
+void OSVideoSurface::playMovie(uint flags, CGameObject *obj) {
+ if (loadIfReady() && _movie)
+ _movie->play(flags, obj);
+
+ _ddSurface->fill(nullptr, 0);
+}
+
+void OSVideoSurface::playMovie(uint startFrame, uint endFrame, uint flags, CGameObject *obj) {
+ if (loadIfReady() && _movie) {
+ _movie->play(startFrame, endFrame, flags, obj);
+ _movie->pause();
+ }
+}
+
+void OSVideoSurface::playMovie(uint startFrame, uint endFrame, uint initialFrame, uint flags, CGameObject *obj) {
+ if (loadIfReady() && _movie) {
+ _movie->play(startFrame, endFrame, initialFrame, flags, obj);
+ }
+}
+
+void OSVideoSurface::stopMovie() {
+ if (_movie)
+ _movie->stop();
+}
+
+void OSVideoSurface::setMovieFrame(uint frameNumber) {
+ if (loadIfReady() && _movie)
+ _movie->setFrame(frameNumber);
+}
+
+void OSVideoSurface::addMovieEvent(int frameNumber, CGameObject *obj) {
+ if (_movie)
+ _movie->addEvent(frameNumber, obj);
+}
+
+void OSVideoSurface::setMovieFrameRate(double rate) {
+ if (_movie)
+ _movie->setFrameRate(rate);
+}
+
+const CMovieRangeInfoList *OSVideoSurface::getMovieRangeInfo() const {
+ return _movie ? _movie->getMovieRangeInfo() : nullptr;
+}
+
+void OSVideoSurface::flipVertically(bool needsLock) {
+ if (!loadIfReady() || !_flipVertically)
+ return;
+
+ if (needsLock)
+ lock();
+
+ byte lineBuffer[SCREEN_WIDTH * 2];
+ int pitch = getBpp() * getWidth();
+ assert(pitch < (SCREEN_WIDTH * 2));
+
+ for (int yp = 0; yp < (_rawSurface->h / 2); ++yp) {
+ byte *line1P = (byte *)_rawSurface->getBasePtr(0, yp);
+ byte *line2P = (byte *)_rawSurface->getBasePtr(0, _rawSurface->h - yp - 1);
+
+ Common::copy(line1P, line1P + pitch, lineBuffer);
+ Common::copy(line2P, line2P + pitch, line1P);
+ Common::copy(lineBuffer, lineBuffer + pitch, line1P);
+ }
+
+ _flipVertically = false;
+ if (needsLock)
+ unlock();
+}
+
+bool OSVideoSurface::loadIfReady() {
+ _videoSurfaceNum = _videoSurfaceCounter;
+
+ if (hasSurface()) {
+ return true;
+ } else if (_pendingLoad) {
+ _hasFrame = true;
+ load();
+ return true;
+ } else {
+ return false;
+ }
+}
+
+void OSVideoSurface::transPixelate() {
+ if (!loadIfReady())
+ return;
+
+ lock();
+ Graphics::ManagedSurface *surface = _rawSurface;
+ uint transColor = getTransparencyColor();
+ // TODO: Check whether color is correct
+ uint pixelColor = surface->format.RGBToColor(0x50, 0, 0);
+
+ for (int yp = 0; yp < surface->h; ++yp) {
+ uint16 *pixelsP = (uint16 *)surface->getBasePtr(0, yp);
+ bool bitFlag = (yp % 2) == 0;
+ int replaceCtr = yp & 3;
+
+ for (int xp = 0; xp < surface->w; ++xp, ++pixelsP) {
+ if (bitFlag && *pixelsP == transColor && replaceCtr == 0)
+ *pixelsP = pixelColor;
+
+ bitFlag = !bitFlag;
+ replaceCtr = (replaceCtr + 1) & 3;
+ }
+ }
+
+ surface->markAllDirty();
+ unlock();
+}
+
+Graphics::ManagedSurface *OSVideoSurface::dupMovieTransparency() const {
+ return _movie ? _movie->duplicateTransparency() : nullptr;
+}
+
+int OSVideoSurface::freeSurface() {
+ if (!_ddSurface)
+ return 0;
+ int surfaceSize = _ddSurface->getSize();
+
+ delete _movie;
+ _movie = nullptr;
+ delete _ddSurface;
+ _ddSurface = nullptr;
+
+ return surfaceSize;
+}
+
+uint16 *OSVideoSurface::getBasePtr(int x, int y) {
+ assert(_rawSurface);
+ return (uint16 *)_rawSurface->getBasePtr(x, y);
+}
+
+} // End of namespace Titanic