diff options
Diffstat (limited to 'engines/titanic/support/video_surface.cpp')
-rw-r--r-- | engines/titanic/support/video_surface.cpp | 663 |
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 |