diff options
author | Johannes Schickel | 2010-10-13 03:57:44 +0000 |
---|---|---|
committer | Johannes Schickel | 2010-10-13 03:57:44 +0000 |
commit | 75e8452b6e6a2bf4fb2f588aa00b428a60d873b5 (patch) | |
tree | f29541d55309487a94bd1d38e8b53bb3dde9aec6 /engines/sword25/fmv | |
parent | 48ee83b88957dab86bc763e9ef21a70179fa8679 (diff) | |
parent | e9f50882ea5b6beeefa994040be9d3bab6a1f107 (diff) | |
download | scummvm-rg350-75e8452b6e6a2bf4fb2f588aa00b428a60d873b5.tar.gz scummvm-rg350-75e8452b6e6a2bf4fb2f588aa00b428a60d873b5.tar.bz2 scummvm-rg350-75e8452b6e6a2bf4fb2f588aa00b428a60d873b5.zip |
OPENGL: Merged from trunk, from rev 52105 to 53396.
This includes an rather hacky attempt to merge all the recent gp2x backend
changes into the branch. I suppose the gp2x backend and probably all new
backends, i.e. gph, dingux etc., might not compile anymore.
Since I have no way of testing those it would be nice if porters could look
into getting those up to speed in this branch.
svn-id: r53399
Diffstat (limited to 'engines/sword25/fmv')
-rw-r--r-- | engines/sword25/fmv/movieplayer.cpp | 154 | ||||
-rw-r--r-- | engines/sword25/fmv/movieplayer.h | 145 | ||||
-rw-r--r-- | engines/sword25/fmv/movieplayer_script.cpp | 163 | ||||
-rw-r--r-- | engines/sword25/fmv/theora_decoder.cpp | 492 | ||||
-rw-r--r-- | engines/sword25/fmv/theora_decoder.h | 152 | ||||
-rw-r--r-- | engines/sword25/fmv/yuvtorgba.cpp | 243 | ||||
-rw-r--r-- | engines/sword25/fmv/yuvtorgba.h | 51 |
7 files changed, 1400 insertions, 0 deletions
diff --git a/engines/sword25/fmv/movieplayer.cpp b/engines/sword25/fmv/movieplayer.cpp new file mode 100644 index 0000000000..f6757b9a8e --- /dev/null +++ b/engines/sword25/fmv/movieplayer.cpp @@ -0,0 +1,154 @@ +/* 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/fmv/movieplayer.h" +#include "sword25/gfx/graphicengine.h" +#include "sword25/gfx/panel.h" +#include "sword25/kernel/kernel.h" +#include "sword25/package/packagemanager.h" +#include "sword25/sfx/soundengine.h" + +namespace Sword25 { + +#define BS_LOG_PREFIX "MOVIEPLAYER" + +#define FLT_EPSILON 1.192092896e-07F /* smallest such that 1.0+FLT_EPSILON != 1.0 */ + +Service *OggTheora_CreateObject(Kernel *pKernel) { + return new MoviePlayer(pKernel); +} + +MoviePlayer::MoviePlayer(Kernel *pKernel) : Service(pKernel), _decoder(g_system->getMixer()) { + if (!registerScriptBindings()) + BS_LOG_ERRORLN("Script bindings could not be registered."); + else + BS_LOGLN("Script bindings registered."); +} + +MoviePlayer::~MoviePlayer() { + _decoder.close(); +} + +bool MoviePlayer::loadMovie(const Common::String &filename, uint z) { + // Get the file and load it into the decoder + Common::SeekableReadStream *in = Kernel::GetInstance()->GetPackage()->getStream(filename); + _decoder.load(in); + + // Ausgabebitmap erstellen + GraphicEngine *pGfx = Kernel::GetInstance()->GetGfx(); + _outputBitmap = pGfx->GetMainPanel()->addDynamicBitmap(_decoder.getWidth(), _decoder.getHeight()); + if (!_outputBitmap.isValid()) { + BS_LOG_ERRORLN("Output bitmap for movie playback could not be created."); + return false; + } + + // Skalierung des Ausgabebitmaps berechnen, so dass es möglichst viel Bildschirmfläche einnimmt. + float screenToVideoWidth = (float)pGfx->GetDisplayWidth() / (float)_outputBitmap->getWidth(); + float screenToVideoHeight = (float)pGfx->GetDisplayHeight() / (float)_outputBitmap->getHeight(); + float scaleFactor = MIN(screenToVideoWidth, screenToVideoHeight); + + if (abs((int)(scaleFactor - 1.0f)) < FLT_EPSILON) + scaleFactor = 1.0f; + + _outputBitmap->setScaleFactor(scaleFactor); + + // Z-Wert setzen + _outputBitmap->setZ(z); + + // Ausgabebitmap auf dem Bildschirm zentrieren + _outputBitmap->setX((pGfx->GetDisplayWidth() - _outputBitmap->getWidth()) / 2); + _outputBitmap->setY((pGfx->GetDisplayHeight() - _outputBitmap->getHeight()) / 2); + + return true; +} + +bool MoviePlayer::unloadMovie() { + _decoder.close(); + _outputBitmap.erase(); + + return true; +} + +bool MoviePlayer::play() { + _decoder.pauseVideo(false); + return true; +} + +bool MoviePlayer::pause() { + _decoder.pauseVideo(true); + return true; +} + +void MoviePlayer::update() { + if (_decoder.isVideoLoaded()) { + Graphics::Surface *s = _decoder.decodeNextFrame(); + + // Transfer the next frame + assert(s->bytesPerPixel == 4); + byte *frameData = (byte *)s->getBasePtr(0, 0); + _outputBitmap->setContent(frameData, s->pitch * s->h, 0, s->pitch); + } +} + +bool MoviePlayer::isMovieLoaded() { + return _decoder.isVideoLoaded(); +} + +bool MoviePlayer::isPaused() { + return _decoder.isPaused(); +} + +float MoviePlayer::getScaleFactor() { + if (_decoder.isVideoLoaded()) + return _outputBitmap->getScaleFactorX(); + else + return 0; +} + +void MoviePlayer::setScaleFactor(float scaleFactor) { + if (_decoder.isVideoLoaded()) { + _outputBitmap->setScaleFactor(scaleFactor); + + // Ausgabebitmap auf dem Bildschirm zentrieren + GraphicEngine *gfxPtr = Kernel::GetInstance()->GetGfx(); + _outputBitmap->setX((gfxPtr->GetDisplayWidth() - _outputBitmap->getWidth()) / 2); + _outputBitmap->setY((gfxPtr->GetDisplayHeight() - _outputBitmap->getHeight()) / 2); + } +} + +double MoviePlayer::getTime() { + return _decoder.getElapsedTime() / 1000.0; +} + +} // End of namespace Sword25 diff --git a/engines/sword25/fmv/movieplayer.h b/engines/sword25/fmv/movieplayer.h new file mode 100644 index 0000000000..96beb648c0 --- /dev/null +++ b/engines/sword25/fmv/movieplayer.h @@ -0,0 +1,145 @@ +/* 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_MOVIEPLAYER_H +#define SWORD25_MOVIEPLAYER_H + +#include "sword25/kernel/common.h" +#include "sword25/kernel/service.h" +#include "sword25/fmv/theora_decoder.h" +#include "sword25/gfx/bitmap.h" + +namespace Sword25 { + +class MoviePlayer : public Service { +public: + // ----------------------------------------------------------------------------- + // Constructor / Destructor + // ----------------------------------------------------------------------------- + + MoviePlayer(Kernel *pKernel); + ~MoviePlayer(); + + // ----------------------------------------------------------------------------- + // Player interface must be implemented by a Movie Player + // ----------------------------------------------------------------------------- + + /** + * Loads a movie file + * + * This method loads a movie file and prepares it for playback. + * There can be oly one movie file loaded at a time. If you already have loaded a + * movie file, it will be unloaded and, if necessary, stopped playing. + * @param Filename The filename of the movie file to be loaded + * @param Z Z indicates the position of the film on the main graphics layer + * @return Returns false if an error occured while loading, otherwise true. + */ + bool loadMovie(const Common::String &filename, uint z); + + /** + * Unloads the currently loaded movie file. + * @return Returns false if an error occurred while unloading, otherwise true. + * @remark This method can only be called when IsMovieLoaded() returns true. + */ + bool unloadMovie(); + + /** + * Plays the loaded movie. + * + * The film will be keeping the aspect ratio of the screen. + * If the film was previously paused with Pause(), then the film will resume playing. + * @return Returns false if an error occurred while starting, otherwise true. + * @remark This method can only be called when IsMovieLoaded() returns true. + */ + bool play(); + + /** + * Pauses movie playback. + * + * A paused movie can later be resumed by calling the Play() method again. + * @return Returns false if an error occurred while pausing, otherwise true. + * @remark This method can only be called when IsMovieLoaded() returns true. + */ + bool pause(); + + /** + * This function must be called once per frame. + */ + void update(); + + /** + * Returns whether a film is loaded for playback. + */ + bool isMovieLoaded(); + + /** + * Returns whether the movie playback is paused. + * @remark This method can only be called when IsMovieLoaded() returns true. + */ + bool isPaused(); + + /** + * Returns the scaling factor for the loaded film. + * + * When a movie is loaded, the scaling factor is automatically selected so that the film + * takes the maximum screen space, without the film being distorted. + * @return Returns the scaling factor of the film. + * @remark This method can only be called when IsMovieLoaded() returns true. + */ + float getScaleFactor(); + + /** + * Sets the factor by which the loaded film is to be scaled. + * @param ScaleFactor The desired scale factor. + * @remark This method can only be called when IsMovieLoaded() returns true. + */ + void setScaleFactor(float scaleFactor); + + /** + * Returns the current playing position in seconds. + * @remark This method can only be called when IsMovieLoaded() returns true. + */ + double getTime(); + +private: + bool registerScriptBindings(); + + TheoraDecoder _decoder; + + RenderObjectPtr<Bitmap> _outputBitmap; +}; + +} // End of namespace Sword25 + +#endif diff --git a/engines/sword25/fmv/movieplayer_script.cpp b/engines/sword25/fmv/movieplayer_script.cpp new file mode 100644 index 0000000000..13bb149672 --- /dev/null +++ b/engines/sword25/fmv/movieplayer_script.cpp @@ -0,0 +1,163 @@ +/* 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/fmv/movieplayer.h" + +namespace Sword25 { + +int loadMovie(lua_State *L) { + MoviePlayer *FMVPtr = Kernel::GetInstance()->GetFMV(); + BS_ASSERT(FMVPtr); + + lua_pushbooleancpp(L, FMVPtr->loadMovie(luaL_checkstring(L, 1), lua_gettop(L) == 2 ? static_cast<uint>(luaL_checknumber(L, 2)) : 10)); + + return 1; +} + +int unloadMovie(lua_State *L) { + MoviePlayer *FMVPtr = Kernel::GetInstance()->GetFMV(); + BS_ASSERT(FMVPtr); + + lua_pushbooleancpp(L, FMVPtr->unloadMovie()); + + return 1; +} + +int play(lua_State *L) { + MoviePlayer *FMVPtr = Kernel::GetInstance()->GetFMV(); + BS_ASSERT(FMVPtr); + + lua_pushbooleancpp(L, FMVPtr->play()); + + return 1; +} + +int pause(lua_State *L) { + MoviePlayer *FMVPtr = Kernel::GetInstance()->GetFMV(); + BS_ASSERT(FMVPtr); + + lua_pushbooleancpp(L, FMVPtr->pause()); + + return 1; +} + +int update(lua_State *L) { + MoviePlayer *FMVPtr = Kernel::GetInstance()->GetFMV(); + BS_ASSERT(FMVPtr); + + FMVPtr->update(); + + return 0; +} + +int isMovieLoaded(lua_State *L) { + MoviePlayer *FMVPtr = Kernel::GetInstance()->GetFMV(); + BS_ASSERT(FMVPtr); + + lua_pushbooleancpp(L, FMVPtr->isMovieLoaded()); + + return 1; +} + +int isPaused(lua_State *L) { + MoviePlayer *FMVPtr = Kernel::GetInstance()->GetFMV(); + BS_ASSERT(FMVPtr); + + lua_pushbooleancpp(L, FMVPtr->isPaused()); + + return 1; +} + +int getScaleFactor(lua_State *L) { + MoviePlayer *FMVPtr = Kernel::GetInstance()->GetFMV(); + BS_ASSERT(FMVPtr); + + lua_pushnumber(L, FMVPtr->getScaleFactor()); + + return 1; +} + +int setScaleFactor(lua_State *L) { + MoviePlayer *FMVPtr = Kernel::GetInstance()->GetFMV(); + BS_ASSERT(FMVPtr); + + FMVPtr->setScaleFactor(static_cast<float>(luaL_checknumber(L, 1))); + + return 0; +} + +int getTime(lua_State *L) { + MoviePlayer *FMVPtr = Kernel::GetInstance()->GetFMV(); + BS_ASSERT(FMVPtr); + + lua_pushnumber(L, FMVPtr->getTime()); + + return 1; +} + +const char *LIBRARY_NAME = "Movieplayer"; + +const luaL_reg LIBRARY_FUNCTIONS[] = { + { "LoadMovie", loadMovie }, + { "UnloadMovie", unloadMovie }, + { "Play", play }, + { "Pause", pause }, + { "Update", update }, + { "IsMovieLoaded", isMovieLoaded }, + { "IsPaused", isPaused }, + { "GetScaleFactor", getScaleFactor }, + { "SetScaleFactor", setScaleFactor }, + { "GetTime", getTime }, + { 0, 0 } +}; + +bool MoviePlayer::registerScriptBindings() { + Kernel *pKernel = Kernel::GetInstance(); + BS_ASSERT(pKernel); + ScriptEngine *pScript = static_cast<ScriptEngine *>(pKernel->GetService("script")); + BS_ASSERT(pScript); + lua_State *L = static_cast<lua_State *>(pScript->getScriptObject()); + BS_ASSERT(L); + + if (!LuaBindhelper::addFunctionsToLib(L, LIBRARY_NAME, LIBRARY_FUNCTIONS)) return false; + + return true; +} + +} // End of namespace Sword25 diff --git a/engines/sword25/fmv/theora_decoder.cpp b/engines/sword25/fmv/theora_decoder.cpp new file mode 100644 index 0000000000..9b1951828e --- /dev/null +++ b/engines/sword25/fmv/theora_decoder.cpp @@ -0,0 +1,492 @@ +/* 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$ + * + */ + +/* + * Source is based on the player example from libvorbis package + * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. + * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2009 + * by the Xiph.Org Foundation and contributors http://www.xiph.org/ + * + */ + +#include "sword25/fmv/theora_decoder.h" +#include "sword25/fmv/yuvtorgba.h" +#include "common/system.h" +#include "sound/decoders/raw.h" + +namespace Sword25 { + +#define AUDIOFD_FRAGSIZE 10240 + +static double rint(double v) { + return floor(v + 0.5); +} + +TheoraDecoder::TheoraDecoder(Audio::Mixer *mixer, Audio::Mixer::SoundType soundType) : _mixer(mixer) { + _fileStream = 0; + _surface = 0; + + _theoraPacket = 0; + _vorbisPacket = 0; + _theoraDecode = 0; + _theoraSetup = 0; + _stateFlag = false; + + _soundType = soundType; + _audStream = 0; + _audHandle = new Audio::SoundHandle(); + + ogg_sync_init(&_oggSync); + + _curFrame = 0; + _audiobuf = (ogg_int16_t *)calloc(AUDIOFD_FRAGSIZE, sizeof(ogg_int16_t)); + + reset(); +} + +TheoraDecoder::~TheoraDecoder() { + close(); + delete _fileStream; + delete _audHandle; + free(_audiobuf); +} + +void TheoraDecoder::queuePage(ogg_page *page) { + if (_theoraPacket) + ogg_stream_pagein(&_theoraOut, page); + + if (_vorbisPacket) + ogg_stream_pagein(&_vorbisOut, page); +} + +int TheoraDecoder::bufferData() { + char *buffer = ogg_sync_buffer(&_oggSync, 4096); + int bytes = _fileStream->read(buffer, 4096); + + ogg_sync_wrote(&_oggSync, bytes); + + return bytes; +} + +bool TheoraDecoder::load(Common::SeekableReadStream *stream) { + close(); + + _fileStream = stream; + + // start up Ogg stream synchronization layer + ogg_sync_init(&_oggSync); + + // init supporting Vorbis structures needed in header parsing + vorbis_info_init(&_vorbisInfo); + vorbis_comment_init(&_vorbisComment); + + // init supporting Theora structures needed in header parsing + th_comment_init(&_theoraComment); + th_info_init(&_theoraInfo); + + // Ogg file open; parse the headers + // Only interested in Vorbis/Theora streams + while (!_stateFlag) { + int ret = bufferData(); + + if (ret == 0) + break; + + while (ogg_sync_pageout(&_oggSync, &_oggPage) > 0) { + ogg_stream_state test; + + // is this a mandated initial header? If not, stop parsing + if (!ogg_page_bos(&_oggPage)) { + // don't leak the page; get it into the appropriate stream + queuePage(&_oggPage); + _stateFlag = true; + break; + } + + ogg_stream_init(&test, ogg_page_serialno(&_oggPage)); + ogg_stream_pagein(&test, &_oggPage); + ogg_stream_packetout(&test, &_oggPacket); + + // identify the codec: try theora + if (!_theoraPacket && th_decode_headerin(&_theoraInfo, &_theoraComment, &_theoraSetup, &_oggPacket) >= 0) { + // it is theora + memcpy(&_theoraOut, &test, sizeof(test)); + _theoraPacket = 1; + } else if (!_vorbisPacket && vorbis_synthesis_headerin(&_vorbisInfo, &_vorbisComment, &_oggPacket) >= 0) { + // it is vorbis + memcpy(&_vorbisOut, &test, sizeof(test)); + _vorbisPacket = 1; + } else { + // whatever it is, we don't care about it + ogg_stream_clear(&test); + } + } + // fall through to non-bos page parsing + } + + // we're expecting more header packets. + while ((_theoraPacket && _theoraPacket < 3) || (_vorbisPacket && _vorbisPacket < 3)) { + int ret; + + // look for further theora headers + while (_theoraPacket && (_theoraPacket < 3) && (ret = ogg_stream_packetout(&_theoraOut, &_oggPacket))) { + if (ret < 0) + error("Error parsing Theora stream headers; corrupt stream?"); + + if (!th_decode_headerin(&_theoraInfo, &_theoraComment, &_theoraSetup, &_oggPacket)) + error("Error parsing Theora stream headers; corrupt stream?"); + + _theoraPacket++; + } + + // look for more vorbis header packets + while (_vorbisPacket && (_vorbisPacket < 3) && (ret = ogg_stream_packetout(&_vorbisOut, &_oggPacket))) { + if (ret < 0) + error("Error parsing Vorbis stream headers; corrupt stream?"); + + if (vorbis_synthesis_headerin(&_vorbisInfo, &_vorbisComment, &_oggPacket)) + error("Error parsing Vorbis stream headers; corrupt stream?"); + + _vorbisPacket++; + + if (_vorbisPacket == 3) + break; + } + + // The header pages/packets will arrive before anything else we + // care about, or the stream is not obeying spec + + if (ogg_sync_pageout(&_oggSync, &_oggPage) > 0) { + queuePage(&_oggPage); // demux into the appropriate stream + } else { + ret = bufferData(); // someone needs more data + + if (ret == 0) + error("End of file while searching for codec headers."); + } + } + + // and now we have it all. initialize decoders + if (_theoraPacket) { + _theoraDecode = th_decode_alloc(&_theoraInfo, _theoraSetup); + debugN(1, "Ogg logical stream %lx is Theora %dx%d %.02f fps", + _theoraOut.serialno, _theoraInfo.pic_width, _theoraInfo.pic_height, + (double)_theoraInfo.fps_numerator / _theoraInfo.fps_denominator); + + switch (_theoraInfo.pixel_fmt) { + case TH_PF_420: + debug(1, " 4:2:0 video"); + break; + case TH_PF_422: + debug(1, " 4:2:2 video"); + break; + case TH_PF_444: + debug(1, " 4:4:4 video"); + break; + case TH_PF_RSVD: + default: + debug(1, " video\n (UNKNOWN Chroma sampling!)"); + break; + } + + if (_theoraInfo.pic_width != _theoraInfo.frame_width || _theoraInfo.pic_height != _theoraInfo.frame_height) + debug(1, " Frame content is %dx%d with offset (%d,%d).", + _theoraInfo.frame_width, _theoraInfo.frame_height, _theoraInfo.pic_x, _theoraInfo.pic_y); + + switch (_theoraInfo.colorspace){ + case TH_CS_UNSPECIFIED: + /* nothing to report */ + break;; + case TH_CS_ITU_REC_470M: + debug(1, " encoder specified ITU Rec 470M (NTSC) color."); + break; + case TH_CS_ITU_REC_470BG: + debug(1, " encoder specified ITU Rec 470BG (PAL) color."); + break; + default: + debug(1, "warning: encoder specified unknown colorspace (%d).", _theoraInfo.colorspace); + break; + } + + debug(1, "Encoded by %s", _theoraComment.vendor); + if (_theoraComment.comments) { + debug(1, "theora comment header:"); + for (int i = 0; i < _theoraComment.comments; i++) { + if (_theoraComment.user_comments[i]) { + int len = _theoraComment.comment_lengths[i]; + char *value = (char *)malloc(len + 1); + memcpy(value, _theoraComment.user_comments[i], len); + value[len] = '\0'; + debug(1, "\t%s", value); + free(value); + } + } + } + + th_decode_ctl(_theoraDecode, TH_DECCTL_GET_PPLEVEL_MAX, &_ppLevelMax, sizeof(_ppLevelMax)); + _ppLevel = _ppLevelMax; + th_decode_ctl(_theoraDecode, TH_DECCTL_SET_PPLEVEL, &_ppLevel, sizeof(_ppLevel)); + _ppInc = 0; + } else { + // tear down the partial theora setup + th_info_clear(&_theoraInfo); + th_comment_clear(&_theoraComment); + } + + th_setup_free(_theoraSetup); + _theoraSetup = 0; + + if (_vorbisPacket) { + vorbis_synthesis_init(&_vorbisDSP, &_vorbisInfo); + vorbis_block_init(&_vorbisDSP, &_vorbisBlock); + debug(3, "Ogg logical stream %lx is Vorbis %d channel %ld Hz audio.", + _vorbisOut.serialno, _vorbisInfo.channels, _vorbisInfo.rate); + } else { + // tear down the partial vorbis setup + vorbis_info_clear(&_vorbisInfo); + vorbis_comment_clear(&_vorbisComment); + } + + // open audio + if (_vorbisPacket) { + _audStream = createAudioStream(); + if (_audStream && _mixer) + _mixer->playStream(_soundType, _audHandle, _audStream); + } + + _surface = new Graphics::Surface(); + + _surface->create(_theoraInfo.frame_width, _theoraInfo.frame_height, 4); + + return true; +} + +void TheoraDecoder::close() { + if (_vorbisPacket) { + ogg_stream_clear(&_vorbisOut); + vorbis_block_clear(&_vorbisBlock); + vorbis_dsp_clear(&_vorbisDSP); + vorbis_comment_clear(&_vorbisComment); + vorbis_info_clear(&_vorbisInfo); + + if (_mixer) + _mixer->stopHandle(*_audHandle); + _audStream = 0; + _vorbisPacket = false; + } + if (_theoraPacket) { + ogg_stream_clear(&_theoraOut); + th_decode_free(_theoraDecode); + th_comment_clear(&_theoraComment); + th_info_clear(&_theoraInfo); + _theoraDecode = 0; + _theoraPacket = false; + } + + if (!_fileStream) + return; + + ogg_sync_clear(&_oggSync); + + delete _fileStream; + _fileStream = 0; + + _surface->free(); + delete _surface; + _surface = 0; + + reset(); +} + +Graphics::Surface *TheoraDecoder::decodeNextFrame() { + int i, j; + +// _stateFlag = false; // playback has not begun + + // we want a video and audio frame ready to go at all times. If + // we have to buffer incoming, buffer the compressed data (ie, let + // ogg do the buffering) + while (_vorbisPacket && !_audiobufReady) { + int ret; + float **pcm; + + // if there's pending, decoded audio, grab it + if ((ret = vorbis_synthesis_pcmout(&_vorbisDSP, &pcm)) > 0) { + int count = _audiobufFill / 2; + int maxsamples = (AUDIOFD_FRAGSIZE - _audiobufFill) / 2 / _vorbisInfo.channels; + for (i = 0; i < ret && i < maxsamples; i++) + for (j = 0; j < _vorbisInfo.channels; j++) { + int val = CLIP((int)rint(pcm[j][i] * 32767.f), -32768, 32768); + _audiobuf[count++] = val; + } + + vorbis_synthesis_read(&_vorbisDSP, i); + _audiobufFill += i * _vorbisInfo.channels * 2; + + if (_audiobufFill == AUDIOFD_FRAGSIZE) + _audiobufReady = true; + + if (_vorbisDSP.granulepos >= 0) + _audiobufGranulePos = _vorbisDSP.granulepos - ret + i; + else + _audiobufGranulePos += i; + } else { + + // no pending audio; is there a pending packet to decode? + if (ogg_stream_packetout(&_vorbisOut, &_oggPacket) > 0) { + if (vorbis_synthesis(&_vorbisBlock, &_oggPacket) == 0) // test for success! + vorbis_synthesis_blockin(&_vorbisDSP, &_vorbisBlock); + } else // we need more data; break out to suck in another page + break; + } + } + + while (_theoraPacket && !_videobufReady) { + // theora is one in, one out... + if (ogg_stream_packetout(&_theoraOut, &_oggPacket) > 0) { + + if (_ppInc) { + _ppLevel += _ppInc; + th_decode_ctl(_theoraDecode, TH_DECCTL_SET_PPLEVEL, &_ppLevel, sizeof(_ppLevel)); + _ppInc = 0; + } + // HACK: This should be set after a seek or a gap, but we might not have + // a granulepos for the first packet (we only have them for the last + // packet on a page), so we just set it as often as we get it. + // To do this right, we should back-track from the last packet on the + // page and compute the correct granulepos for the first packet after + // a seek or a gap. + if (_oggPacket.granulepos >= 0) { + th_decode_ctl(_theoraDecode, TH_DECCTL_SET_GRANPOS, &_oggPacket.granulepos, sizeof(_oggPacket.granulepos)); + } + if (th_decode_packetin(_theoraDecode, &_oggPacket, &_videobufGranulePos) == 0) { + _videobufTime = th_granule_time(_theoraDecode, _videobufGranulePos); + _curFrame++; + + _videobufReady = true; + } + } else + break; + } + + if (!_videobufReady && !_audiobufReady && _fileStream->eos()) { + close(); + return _surface; + } + + if (!_videobufReady || !_audiobufReady) { + // no data yet for somebody. Grab another page + bufferData(); + while (ogg_sync_pageout(&_oggSync, &_oggPage) > 0) { + queuePage(&_oggPage); + } + } + + // If playback has begun, top audio buffer off immediately. +/* FIXME: This is currently crashing + if (_stateFlag) { + _audStream->queueBuffer((byte *)_audiobuf, AUDIOFD_FRAGSIZE, DisposeAfterUse::NO, Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN | Audio::FLAG_STEREO); + } +*/ + + // are we at or past time for this video frame? + if (_stateFlag && _videobufReady) { + th_ycbcr_buffer yuv; + + th_decode_ycbcr_out(_theoraDecode, yuv); + + // Convert YUV data to RGB data + YUVtoBGRA::translate(yuv, _theoraInfo, (byte *)_surface->getBasePtr(0, 0), _surface->pitch * _surface->h); + + switch (_theoraInfo.pixel_fmt) { + case TH_PF_420: + break; + case TH_PF_422: + break; + case TH_PF_444: + break; + default: + break; + } + + _videobufReady = false; + } + + // if our buffers either don't exist or are ready to go, + // we can begin playback + if ((!_theoraPacket || _videobufReady) && + (!_vorbisPacket || _audiobufReady)) + _stateFlag = true; + + // same if we've run out of input + if (_fileStream->eos()) + _stateFlag = true; + + return _surface; +} + +void TheoraDecoder::reset() { + VideoDecoder::reset(); + + if (_fileStream) + _fileStream->seek(0); + + _videobufReady = false; + _videobufGranulePos = -1; + _videobufTime = 0; + + _audiobufFill = 0; + _audiobufReady = false; + _audiobufGranulePos = 0; + + _curFrame = 0; + + _theoraPacket = 0; + _vorbisPacket = 0; + _stateFlag = false; +} + +bool TheoraDecoder::endOfVideo() const { + return !isVideoLoaded(); +} + + +uint32 TheoraDecoder::getElapsedTime() const { + if (_audStream && _mixer) + return _mixer->getSoundElapsedTime(*_audHandle); + + return VideoDecoder::getElapsedTime(); +} + +Audio::QueuingAudioStream *TheoraDecoder::createAudioStream() { + return Audio::makeQueuingAudioStream(_vorbisInfo.rate, _vorbisInfo.channels); +} + +} // End of namespace Sword25 diff --git a/engines/sword25/fmv/theora_decoder.h b/engines/sword25/fmv/theora_decoder.h new file mode 100644 index 0000000000..12d8035c0a --- /dev/null +++ b/engines/sword25/fmv/theora_decoder.h @@ -0,0 +1,152 @@ +/* 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$ + * + */ + +#ifndef SWORD25_THEORADECODER_H +#define SWORD25_THEORADECODER_H + +#include "graphics/video/video_decoder.h" +#include "sound/audiostream.h" +#include "sound/mixer.h" + +#include <theora/theoradec.h> +#include <vorbis/codec.h> + +namespace Common { +class SeekableReadStream; +} + +namespace Sword25 { + +/** + * + * Decoder for Theora videos. + * Video decoder used in engines: + * - sword25 + */ +class TheoraDecoder : public Graphics::FixedRateVideoDecoder { +public: + TheoraDecoder(Audio::Mixer *mixer = 0, Audio::Mixer::SoundType soundType = Audio::Mixer::kMusicSoundType); + virtual ~TheoraDecoder(); + + /** + * Load a video file + * @param stream the stream to load + */ + bool load(Common::SeekableReadStream *stream); + void close(); + void reset(); + + /** + * Decode the next frame and return the frame's surface + * @note the return surface should *not* be freed + * @note this may return 0, in which case the last frame should be kept on screen + */ + Graphics::Surface *decodeNextFrame(); + + bool isVideoLoaded() const { + return _fileStream != 0; + } + bool isPaused() const { + return (VideoDecoder::isPaused() || !isVideoLoaded()); + } + + uint16 getWidth() const { + return _surface->w; + } + uint16 getHeight() const { + return _surface->h; + } + uint32 getFrameCount() const { + // It is not possible to get frame count easily + // I.e. seeking is required + assert(0); + return 0; + } + Graphics::PixelFormat getPixelFormat() const { + return Graphics::PixelFormat(4, 8, 8, 8, 8, 16, 8, 0, 24); + } + + uint32 getElapsedTime() const; + + bool endOfVideo() const; + +protected: + Common::Rational getFrameRate() const { + return _frameRate; + } + +private: + void queuePage(ogg_page *page); + int bufferData(); + Audio::QueuingAudioStream *createAudioStream(); + +private: + Common::SeekableReadStream *_fileStream; + Graphics::Surface *_surface; + Common::Rational _frameRate; + uint32 _frameCount; + + Audio::Mixer *_mixer; + Audio::Mixer::SoundType _soundType; + Audio::SoundHandle *_audHandle; + Audio::QueuingAudioStream *_audStream; + + ogg_sync_state _oggSync; + ogg_page _oggPage; + ogg_packet _oggPacket; + ogg_stream_state _vorbisOut; + ogg_stream_state _theoraOut; + th_info _theoraInfo; + th_comment _theoraComment; + th_dec_ctx *_theoraDecode; + th_setup_info *_theoraSetup; + vorbis_info _vorbisInfo; + vorbis_dsp_state _vorbisDSP; + vorbis_block _vorbisBlock; + vorbis_comment _vorbisComment; + + int _theoraPacket; + int _vorbisPacket; + bool _stateFlag; + + int _ppLevelMax; + int _ppLevel; + int _ppInc; + + // single frame video buffering + bool _videobufReady; + ogg_int64_t _videobufGranulePos; + double _videobufTime; + + // single audio fragment audio buffering + int _audiobufFill; + bool _audiobufReady; + ogg_int16_t *_audiobuf; + ogg_int64_t _audiobufGranulePos; // time position of last sample +}; + +} // End of namespace Sword25 + +#endif diff --git a/engines/sword25/fmv/yuvtorgba.cpp b/engines/sword25/fmv/yuvtorgba.cpp new file mode 100644 index 0000000000..12d11b6784 --- /dev/null +++ b/engines/sword25/fmv/yuvtorgba.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 + * + */ + +#include "sword25/fmv/yuvtorgba.h" + +namespace Sword25 { + +static const int PRECISION = 32768; +static const int COEFFS_Y[256] = { + -593888, -555746, -517604, -479462, -441320, -403178, -365036, -326894, -288752, -250610, -212468, -174326, -136184, -98042, -59900, -21758, + 16384, 54526, 92668, 130810, 168952, 207094, 245236, 283378, 321520, 359662, 397804, 435946, 474088, 512230, 550372, 588514, + 626656, 664798, 702940, 741082, 779224, 817366, 855508, 893650, 931792, 969934, 1008076, 1046218, 1084360, 1122502, 1160644, 1198786, + 1236928, 1275070, 1313212, 1351354, 1389496, 1427638, 1465780, 1503922, 1542064, 1580206, 1618348, 1656490, 1694632, 1732774, 1770916, 1809058, + 1847200, 1885342, 1923484, 1961626, 1999768, 2037910, 2076052, 2114194, 2152336, 2190478, 2228620, 2266762, 2304904, 2343046, 2381188, 2419330, + 2457472, 2495614, 2533756, 2571898, 2610040, 2648182, 2686324, 2724466, 2762608, 2800750, 2838892, 2877034, 2915176, 2953318, 2991460, 3029602, + 3067744, 3105886, 3144028, 3182170, 3220312, 3258454, 3296596, 3334738, 3372880, 3411022, 3449164, 3487306, 3525448, 3563590, 3601732, 3639874, + 3678016, 3716158, 3754300, 3792442, 3830584, 3868726, 3906868, 3945010, 3983152, 4021294, 4059436, 4097578, 4135720, 4173862, 4212004, 4250146, + 4288288, 4326430, 4364572, 4402714, 4440856, 4478998, 4517140, 4555282, 4593424, 4631566, 4669708, 4707850, 4745992, 4784134, 4822276, 4860418, + 4898560, 4936702, 4974844, 5012986, 5051128, 5089270, 5127412, 5165554, 5203696, 5241838, 5279980, 5318122, 5356264, 5394406, 5432548, 5470690, + 5508832, 5546974, 5585116, 5623258, 5661400, 5699542, 5737684, 5775826, 5813968, 5852110, 5890252, 5928394, 5966536, 6004678, 6042820, 6080962, + 6119104, 6157246, 6195388, 6233530, 6271672, 6309814, 6347956, 6386098, 6424240, 6462382, 6500524, 6538666, 6576808, 6614950, 6653092, 6691234, + 6729376, 6767518, 6805660, 6843802, 6881944, 6920086, 6958228, 6996370, 7034512, 7072654, 7110796, 7148938, 7187080, 7225222, 7263364, 7301506, + 7339648, 7377790, 7415932, 7454074, 7492216, 7530358, 7568500, 7606642, 7644784, 7682926, 7721068, 7759210, 7797352, 7835494, 7873636, 7911778, + 7949920, 7988062, 8026204, 8064346, 8102488, 8140630, 8178772, 8216914, 8255056, 8293198, 8331340, 8369482, 8407624, 8445766, 8483908, 8522050, + 8560192, 8598334, 8636476, 8674618, 8712760, 8750902, 8789044, 8827186, 8865328, 8903470, 8941612, 8979754, 9017896, 9056038, 9094180, 9132322, +}; +static const int COEFFS_RV[256] = { + -6694144, -6641846, -6589548, -6537250, -6484952, -6432654, -6380356, -6328058, -6275760, -6223462, -6171164, -6118866, -6066568, -6014270, -5961972, -5909674, + -5857376, -5805078, -5752780, -5700482, -5648184, -5595886, -5543588, -5491290, -5438992, -5386694, -5334396, -5282098, -5229800, -5177502, -5125204, -5072906, + -5020608, -4968310, -4916012, -4863714, -4811416, -4759118, -4706820, -4654522, -4602224, -4549926, -4497628, -4445330, -4393032, -4340734, -4288436, -4236138, + -4183840, -4131542, -4079244, -4026946, -3974648, -3922350, -3870052, -3817754, -3765456, -3713158, -3660860, -3608562, -3556264, -3503966, -3451668, -3399370, + -3347072, -3294774, -3242476, -3190178, -3137880, -3085582, -3033284, -2980986, -2928688, -2876390, -2824092, -2771794, -2719496, -2667198, -2614900, -2562602, + -2510304, -2458006, -2405708, -2353410, -2301112, -2248814, -2196516, -2144218, -2091920, -2039622, -1987324, -1935026, -1882728, -1830430, -1778132, -1725834, + -1673536, -1621238, -1568940, -1516642, -1464344, -1412046, -1359748, -1307450, -1255152, -1202854, -1150556, -1098258, -1045960, -993662, -941364, -889066, + -836768, -784470, -732172, -679874, -627576, -575278, -522980, -470682, -418384, -366086, -313788, -261490, -209192, -156894, -104596, -52298, + 0, 52298, 104596, 156894, 209192, 261490, 313788, 366086, 418384, 470682, 522980, 575278, 627576, 679874, 732172, 784470, + 836768, 889066, 941364, 993662, 1045960, 1098258, 1150556, 1202854, 1255152, 1307450, 1359748, 1412046, 1464344, 1516642, 1568940, 1621238, + 1673536, 1725834, 1778132, 1830430, 1882728, 1935026, 1987324, 2039622, 2091920, 2144218, 2196516, 2248814, 2301112, 2353410, 2405708, 2458006, + 2510304, 2562602, 2614900, 2667198, 2719496, 2771794, 2824092, 2876390, 2928688, 2980986, 3033284, 3085582, 3137880, 3190178, 3242476, 3294774, + 3347072, 3399370, 3451668, 3503966, 3556264, 3608562, 3660860, 3713158, 3765456, 3817754, 3870052, 3922350, 3974648, 4026946, 4079244, 4131542, + 4183840, 4236138, 4288436, 4340734, 4393032, 4445330, 4497628, 4549926, 4602224, 4654522, 4706820, 4759118, 4811416, 4863714, 4916012, 4968310, + 5020608, 5072906, 5125204, 5177502, 5229800, 5282098, 5334396, 5386694, 5438992, 5491290, 5543588, 5595886, 5648184, 5700482, 5752780, 5805078, + 5857376, 5909674, 5961972, 6014270, 6066568, 6118866, 6171164, 6223462, 6275760, 6328058, 6380356, 6432654, 6484952, 6537250, 6589548, 6641846, +}; +static const int COEFFS_GU[256] = { + 1639936, 1627124, 1614312, 1601500, 1588688, 1575876, 1563064, 1550252, 1537440, 1524628, 1511816, 1499004, 1486192, 1473380, 1460568, 1447756, + 1434944, 1422132, 1409320, 1396508, 1383696, 1370884, 1358072, 1345260, 1332448, 1319636, 1306824, 1294012, 1281200, 1268388, 1255576, 1242764, + 1229952, 1217140, 1204328, 1191516, 1178704, 1165892, 1153080, 1140268, 1127456, 1114644, 1101832, 1089020, 1076208, 1063396, 1050584, 1037772, + 1024960, 1012148, 999336, 986524, 973712, 960900, 948088, 935276, 922464, 909652, 896840, 884028, 871216, 858404, 845592, 832780, + 819968, 807156, 794344, 781532, 768720, 755908, 743096, 730284, 717472, 704660, 691848, 679036, 666224, 653412, 640600, 627788, + 614976, 602164, 589352, 576540, 563728, 550916, 538104, 525292, 512480, 499668, 486856, 474044, 461232, 448420, 435608, 422796, + 409984, 397172, 384360, 371548, 358736, 345924, 333112, 320300, 307488, 294676, 281864, 269052, 256240, 243428, 230616, 217804, + 204992, 192180, 179368, 166556, 153744, 140932, 128120, 115308, 102496, 89684, 76872, 64060, 51248, 38436, 25624, 12812, + 0, -12812, -25624, -38436, -51248, -64060, -76872, -89684, -102496, -115308, -128120, -140932, -153744, -166556, -179368, -192180, + -204992, -217804, -230616, -243428, -256240, -269052, -281864, -294676, -307488, -320300, -333112, -345924, -358736, -371548, -384360, -397172, + -409984, -422796, -435608, -448420, -461232, -474044, -486856, -499668, -512480, -525292, -538104, -550916, -563728, -576540, -589352, -602164, + -614976, -627788, -640600, -653412, -666224, -679036, -691848, -704660, -717472, -730284, -743096, -755908, -768720, -781532, -794344, -807156, + -819968, -832780, -845592, -858404, -871216, -884028, -896840, -909652, -922464, -935276, -948088, -960900, -973712, -986524, -999336, -1012148, + -1024960, -1037772, -1050584, -1063396, -1076208, -1089020, -1101832, -1114644, -1127456, -1140268, -1153080, -1165892, -1178704, -1191516, -1204328, -1217140, + -1229952, -1242764, -1255576, -1268388, -1281200, -1294012, -1306824, -1319636, -1332448, -1345260, -1358072, -1370884, -1383696, -1396508, -1409320, -1422132, + -1434944, -1447756, -1460568, -1473380, -1486192, -1499004, -1511816, -1524628, -1537440, -1550252, -1563064, -1575876, -1588688, -1601500, -1614312, -1627124, +}; +static const int COEFFS_GV[256] = { + 3409920, 3383280, 3356640, 3330000, 3303360, 3276720, 3250080, 3223440, 3196800, 3170160, 3143520, 3116880, 3090240, 3063600, 3036960, 3010320, + 2983680, 2957040, 2930400, 2903760, 2877120, 2850480, 2823840, 2797200, 2770560, 2743920, 2717280, 2690640, 2664000, 2637360, 2610720, 2584080, + 2557440, 2530800, 2504160, 2477520, 2450880, 2424240, 2397600, 2370960, 2344320, 2317680, 2291040, 2264400, 2237760, 2211120, 2184480, 2157840, + 2131200, 2104560, 2077920, 2051280, 2024640, 1998000, 1971360, 1944720, 1918080, 1891440, 1864800, 1838160, 1811520, 1784880, 1758240, 1731600, + 1704960, 1678320, 1651680, 1625040, 1598400, 1571760, 1545120, 1518480, 1491840, 1465200, 1438560, 1411920, 1385280, 1358640, 1332000, 1305360, + 1278720, 1252080, 1225440, 1198800, 1172160, 1145520, 1118880, 1092240, 1065600, 1038960, 1012320, 985680, 959040, 932400, 905760, 879120, + 852480, 825840, 799200, 772560, 745920, 719280, 692640, 666000, 639360, 612720, 586080, 559440, 532800, 506160, 479520, 452880, + 426240, 399600, 372960, 346320, 319680, 293040, 266400, 239760, 213120, 186480, 159840, 133200, 106560, 79920, 53280, 26640, + 0, -26640, -53280, -79920, -106560, -133200, -159840, -186480, -213120, -239760, -266400, -293040, -319680, -346320, -372960, -399600, + -426240, -452880, -479520, -506160, -532800, -559440, -586080, -612720, -639360, -666000, -692640, -719280, -745920, -772560, -799200, -825840, + -852480, -879120, -905760, -932400, -959040, -985680, -1012320, -1038960, -1065600, -1092240, -1118880, -1145520, -1172160, -1198800, -1225440, -1252080, + -1278720, -1305360, -1332000, -1358640, -1385280, -1411920, -1438560, -1465200, -1491840, -1518480, -1545120, -1571760, -1598400, -1625040, -1651680, -1678320, + -1704960, -1731600, -1758240, -1784880, -1811520, -1838160, -1864800, -1891440, -1918080, -1944720, -1971360, -1998000, -2024640, -2051280, -2077920, -2104560, + -2131200, -2157840, -2184480, -2211120, -2237760, -2264400, -2291040, -2317680, -2344320, -2370960, -2397600, -2424240, -2450880, -2477520, -2504160, -2530800, + -2557440, -2584080, -2610720, -2637360, -2664000, -2690640, -2717280, -2743920, -2770560, -2797200, -2823840, -2850480, -2877120, -2903760, -2930400, -2957040, + -2983680, -3010320, -3036960, -3063600, -3090240, -3116880, -3143520, -3170160, -3196800, -3223440, -3250080, -3276720, -3303360, -3330000, -3356640, -3383280, +}; +static const int COEFFS_BU[256] = { + -8464128, -8398002, -8331876, -8265750, -8199624, -8133498, -8067372, -8001246, -7935120, -7868994, -7802868, -7736742, -7670616, -7604490, -7538364, -7472238, + -7406112, -7339986, -7273860, -7207734, -7141608, -7075482, -7009356, -6943230, -6877104, -6810978, -6744852, -6678726, -6612600, -6546474, -6480348, -6414222, + -6348096, -6281970, -6215844, -6149718, -6083592, -6017466, -5951340, -5885214, -5819088, -5752962, -5686836, -5620710, -5554584, -5488458, -5422332, -5356206, + -5290080, -5223954, -5157828, -5091702, -5025576, -4959450, -4893324, -4827198, -4761072, -4694946, -4628820, -4562694, -4496568, -4430442, -4364316, -4298190, + -4232064, -4165938, -4099812, -4033686, -3967560, -3901434, -3835308, -3769182, -3703056, -3636930, -3570804, -3504678, -3438552, -3372426, -3306300, -3240174, + -3174048, -3107922, -3041796, -2975670, -2909544, -2843418, -2777292, -2711166, -2645040, -2578914, -2512788, -2446662, -2380536, -2314410, -2248284, -2182158, + -2116032, -2049906, -1983780, -1917654, -1851528, -1785402, -1719276, -1653150, -1587024, -1520898, -1454772, -1388646, -1322520, -1256394, -1190268, -1124142, + -1058016, -991890, -925764, -859638, -793512, -727386, -661260, -595134, -529008, -462882, -396756, -330630, -264504, -198378, -132252, -66126, + 0, 66126, 132252, 198378, 264504, 330630, 396756, 462882, 529008, 595134, 661260, 727386, 793512, 859638, 925764, 991890, + 1058016, 1124142, 1190268, 1256394, 1322520, 1388646, 1454772, 1520898, 1587024, 1653150, 1719276, 1785402, 1851528, 1917654, 1983780, 2049906, + 2116032, 2182158, 2248284, 2314410, 2380536, 2446662, 2512788, 2578914, 2645040, 2711166, 2777292, 2843418, 2909544, 2975670, 3041796, 3107922, + 3174048, 3240174, 3306300, 3372426, 3438552, 3504678, 3570804, 3636930, 3703056, 3769182, 3835308, 3901434, 3967560, 4033686, 4099812, 4165938, + 4232064, 4298190, 4364316, 4430442, 4496568, 4562694, 4628820, 4694946, 4761072, 4827198, 4893324, 4959450, 5025576, 5091702, 5157828, 5223954, + 5290080, 5356206, 5422332, 5488458, 5554584, 5620710, 5686836, 5752962, 5819088, 5885214, 5951340, 6017466, 6083592, 6149718, 6215844, 6281970, + 6348096, 6414222, 6480348, 6546474, 6612600, 6678726, 6744852, 6810978, 6877104, 6943230, 7009356, 7075482, 7141608, 7207734, 7273860, 7339986, + 7406112, 7472238, 7538364, 7604490, 7670616, 7736742, 7802868, 7868994, 7935120, 8001246, 8067372, 8133498, 8199624, 8265750, 8331876, 8398002, +}; +static const int CLAMP_TAB[1024] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, + 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, + 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, + 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, + 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, +}; + +void YUVtoBGRA::translate(th_ycbcr_buffer &YUVBuffer, const th_info &theoraInfo, byte *pixelData, int pixelsSize) { + + // Width and height of all buffers have to be divisible by 2. + BS_ASSERT((YUVBuffer[0].width & 1) == 0); + BS_ASSERT((YUVBuffer[0].height & 1) == 0); + BS_ASSERT((YUVBuffer[1].width & 1) == 0); + BS_ASSERT((YUVBuffer[2].width & 1) == 0); + + // UV images have to have a quarter of the Y image resolution + BS_ASSERT(YUVBuffer[1].width == YUVBuffer[0].width >> 1); + BS_ASSERT(YUVBuffer[2].width == YUVBuffer[0].width >> 1); + BS_ASSERT(YUVBuffer[1].height == YUVBuffer[0].height >> 1); + BS_ASSERT(YUVBuffer[2].height == YUVBuffer[0].height >> 1); + + const int *cl = &CLAMP_TAB[320]; + + const byte *ySrc0 = YUVBuffer[0].data; + const byte *ySrc1 = YUVBuffer[0].data + YUVBuffer[0].stride; + const byte *uSrc = YUVBuffer[1].data; + const byte *vSrc = YUVBuffer[2].data; + byte *dst0 = &pixelData[0]; + byte *dst1 = &pixelData[0] + YUVBuffer[0].width * 4; + + for (int h = 0; h < YUVBuffer[0].height / 2; ++h) { + for (int w = 0; w < YUVBuffer[0].width / 2; ++w) { + int u = *uSrc++; + int v = *vSrc++; + + int rUV = COEFFS_RV[v]; + int gUV = COEFFS_GU[u] + COEFFS_GV[v]; + int bUV = COEFFS_BU[u]; + + int y = *ySrc0++; + int r = COEFFS_Y[y] + rUV; + int g = COEFFS_Y[y] + gUV; + int b = COEFFS_Y[y] + bUV; + *dst0++ = cl[b / PRECISION]; + *dst0++ = cl[g / PRECISION]; + *dst0++ = cl[r / PRECISION]; + *dst0++ = 255; + + y = *ySrc1++; + r = COEFFS_Y[y] + rUV; + g = COEFFS_Y[y] + gUV; + b = COEFFS_Y[y] + bUV; + *dst1++ = cl[b / PRECISION]; + *dst1++ = cl[g / PRECISION]; + *dst1++ = cl[r / PRECISION]; + *dst1++ = 255; + + y = *ySrc0++; + r = COEFFS_Y[y] + rUV; + g = COEFFS_Y[y] + gUV; + b = COEFFS_Y[y] + bUV; + *dst0++ = cl[b / PRECISION]; + *dst0++ = cl[g / PRECISION]; + *dst0++ = cl[r / PRECISION]; + *dst0++ = 255; + + y = *ySrc1++; + r = COEFFS_Y[y] + rUV; + g = COEFFS_Y[y] + gUV; + b = COEFFS_Y[y] + bUV; + *dst1++ = cl[b / PRECISION]; + *dst1++ = cl[g / PRECISION]; + *dst1++ = cl[r / PRECISION]; + *dst1++ = 255; + } + + dst0 += YUVBuffer[0].width * 4; + dst1 += YUVBuffer[0].width * 4; + ySrc0 += YUVBuffer[0].stride * 2 - YUVBuffer[0].width; + ySrc1 += YUVBuffer[0].stride * 2 - YUVBuffer[0].width; + uSrc += YUVBuffer[1].stride - YUVBuffer[1].width; + vSrc += YUVBuffer[2].stride - YUVBuffer[2].width; + } +} + +} // End of namespace Sword25 diff --git a/engines/sword25/fmv/yuvtorgba.h b/engines/sword25/fmv/yuvtorgba.h new file mode 100644 index 0000000000..15fa85b7e4 --- /dev/null +++ b/engines/sword25/fmv/yuvtorgba.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_YUVTORGBA_H +#define SWORD25_YUVTORGBA_H + +#include "sword25/kernel/common.h" +#include <theora/theora.h> +#include <theora/codec.h> + +namespace Sword25 { + +class YUVtoBGRA { +public: + static void translate(th_ycbcr_buffer &YUVBuffer, const th_info &theoraInfo, byte *pixelData, int pixelsSize); +}; + +} // End of namespace Sword25 + +#endif |