mirror of
https://github.com/libretro/scummvm.git
synced 2025-01-02 07:28:32 +00:00
289 lines
8.2 KiB
C++
289 lines
8.2 KiB
C++
/* 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 "pegasus/pegasus.h"
|
|
#include "pegasus/video.h"
|
|
|
|
#include "common/events.h"
|
|
#include "common/textconsole.h"
|
|
#include "graphics/scaler.h"
|
|
#include "video/qt_decoder.h"
|
|
|
|
namespace Pegasus {
|
|
|
|
VideoManager::VideoManager(PegasusEngine *vm) : _vm(vm) {
|
|
_timeZoneVideo = 0;
|
|
}
|
|
|
|
VideoManager::~VideoManager() {
|
|
stopVideos();
|
|
delete _timeZoneVideo;
|
|
}
|
|
|
|
bool VideoManager::loadTimeZoneVideo(const Common::String &filename) {
|
|
Video::QuickTimeDecoder *video = new Video::QuickTimeDecoder();
|
|
|
|
if (!video->loadFile(filename)) {
|
|
delete video;
|
|
return false;
|
|
}
|
|
|
|
delete _timeZoneVideo;
|
|
_timeZoneVideo = video;
|
|
|
|
// Set it on pause
|
|
_timeZoneVideo->pauseVideo(true);
|
|
return true;
|
|
}
|
|
|
|
void VideoManager::drawTimeZoneVideoFrame(uint32 time) {
|
|
assert(_timeZoneVideo);
|
|
|
|
if (!_timeZoneVideo->isPaused())
|
|
_timeZoneVideo->pauseVideo(true);
|
|
|
|
_timeZoneVideo->seekToTime(Audio::Timestamp(0, time, 600));
|
|
|
|
const Graphics::Surface *frame = _timeZoneVideo->decodeNextFrame();
|
|
|
|
if (!frame)
|
|
error("Could not find frame at time %d", time);
|
|
|
|
copyFrameToScreen(frame, _timeZoneVideo->getWidth(), _timeZoneVideo->getHeight(), kViewScreenOffset, kViewScreenOffset);
|
|
_vm->_system->updateScreen();
|
|
}
|
|
|
|
void VideoManager::playTimeZoneVideoSegment(uint32 startTime, uint32 endTime) {
|
|
assert(_timeZoneVideo);
|
|
|
|
_timeZoneVideo->seekToTime(Audio::Timestamp(0, startTime, 600));
|
|
|
|
if (_timeZoneVideo->isPaused())
|
|
_timeZoneVideo->pauseVideo(false);
|
|
|
|
// Convert the end time to ms
|
|
endTime = Audio::Timestamp(0, endTime, 600).msecs();
|
|
|
|
bool continuePlaying = true;
|
|
while (!_timeZoneVideo->endOfVideo() && _timeZoneVideo->getElapsedTime() < endTime && !_vm->shouldQuit() && continuePlaying) {
|
|
if (_timeZoneVideo->needsUpdate()) {
|
|
const Graphics::Surface *frame = _timeZoneVideo->decodeNextFrame();
|
|
|
|
if (frame) {
|
|
copyFrameToScreen(frame, _timeZoneVideo->getWidth(), _timeZoneVideo->getHeight(), kViewScreenOffset, kViewScreenOffset);
|
|
_vm->_system->updateScreen();
|
|
}
|
|
}
|
|
|
|
Common::Event event;
|
|
while (_vm->_system->getEventManager()->pollEvent(event)) {
|
|
switch (event.type) {
|
|
case Common::EVENT_RTL:
|
|
case Common::EVENT_QUIT:
|
|
continuePlaying = false;
|
|
break;
|
|
case Common::EVENT_KEYDOWN:
|
|
switch (event.kbd.keycode) {
|
|
case Common::KEYCODE_ESCAPE:
|
|
continuePlaying = false;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Cut down on CPU usage
|
|
_vm->_system->delayMillis(10);
|
|
}
|
|
|
|
_timeZoneVideo->pauseVideo(true);
|
|
}
|
|
|
|
void VideoManager::pauseVideos() {
|
|
for (uint16 i = 0; i < _videoStreams.size(); i++)
|
|
_videoStreams[i]->pauseVideo(true);
|
|
}
|
|
|
|
void VideoManager::resumeVideos() {
|
|
for (uint16 i = 0; i < _videoStreams.size(); i++)
|
|
_videoStreams[i]->pauseVideo(false);
|
|
}
|
|
|
|
void VideoManager::stopVideos() {
|
|
for (uint16 i = 0; i < _videoStreams.size(); i++) {
|
|
delete _videoStreams[i].video;
|
|
_videoStreams[i].video = 0;
|
|
}
|
|
}
|
|
|
|
void VideoManager::playMovie(Common::String filename, uint16 x, uint16 y) {
|
|
VideoHandle videoHandle = playBackgroundMovie(filename, x, y, false);
|
|
|
|
if (videoHandle != NULL_VID_HANDLE)
|
|
waitUntilMovieEnds(videoHandle);
|
|
}
|
|
|
|
void VideoManager::playMovieCentered(Common::String filename) {
|
|
VideoHandle videoHandle = playBackgroundMovie(filename, 0, 0, false);
|
|
|
|
if (videoHandle == NULL_VID_HANDLE)
|
|
return;
|
|
|
|
_videoStreams[videoHandle].x = (_vm->_system->getWidth() - _videoStreams[videoHandle]->getWidth()) / 2;
|
|
_videoStreams[videoHandle].y = (_vm->_system->getHeight() - _videoStreams[videoHandle]->getHeight()) / 2;
|
|
|
|
waitUntilMovieEnds(videoHandle);
|
|
}
|
|
|
|
void VideoManager::waitUntilMovieEnds(VideoHandle videoHandle) {
|
|
bool continuePlaying = true;
|
|
|
|
while (!_videoStreams[videoHandle]->endOfVideo() && !_vm->shouldQuit() && continuePlaying) {
|
|
if (updateBackgroundMovies())
|
|
_vm->_system->updateScreen();
|
|
|
|
Common::Event event;
|
|
while (_vm->_system->getEventManager()->pollEvent(event)) {
|
|
switch (event.type) {
|
|
case Common::EVENT_RTL:
|
|
case Common::EVENT_QUIT:
|
|
continuePlaying = false;
|
|
break;
|
|
case Common::EVENT_KEYDOWN:
|
|
switch (event.kbd.keycode) {
|
|
case Common::KEYCODE_ESCAPE:
|
|
continuePlaying = false;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Cut down on CPU usage
|
|
_vm->_system->delayMillis(10);
|
|
}
|
|
|
|
delete _videoStreams[videoHandle].video;
|
|
_videoStreams.clear();
|
|
}
|
|
|
|
bool VideoManager::updateBackgroundMovies() {
|
|
bool updateScreen = false;
|
|
|
|
for (uint32 i = 0; i < _videoStreams.size() && !_vm->shouldQuit(); i++) {
|
|
// Skip deleted videos
|
|
if (!_videoStreams[i].video)
|
|
continue;
|
|
|
|
// Remove any videos that are over
|
|
if (_videoStreams[i]->endOfVideo()) {
|
|
if (_videoStreams[i].loop) {
|
|
_videoStreams[i]->rewind();
|
|
} else {
|
|
delete _videoStreams[i].video;
|
|
memset(&_videoStreams[i], 0, sizeof(VideoEntry));
|
|
_videoStreams[i].video = NULL;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// Check if we need to draw a frame
|
|
if (_videoStreams[i]->needsUpdate()) {
|
|
const Graphics::Surface *frame = _videoStreams[i]->decodeNextFrame();
|
|
|
|
if (frame) {
|
|
copyFrameToScreen(frame, _videoStreams[i]->getWidth(), _videoStreams[i]->getHeight(), _videoStreams[i].x, _videoStreams[i].y);
|
|
|
|
// We've drawn something to the screen, make sure we update it
|
|
updateScreen = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Return true if we need to update the screen
|
|
return updateScreen;
|
|
}
|
|
|
|
VideoHandle VideoManager::playBackgroundMovie(Common::String filename, int x, int y, bool loop) {
|
|
// First, check to see if that video is already playing
|
|
for (uint32 i = 0; i < _videoStreams.size(); i++)
|
|
if (_videoStreams[i].filename == filename)
|
|
return i;
|
|
|
|
// Otherwise, create a new entry
|
|
VideoEntry entry;
|
|
entry.video = new Video::QuickTimeDecoder();
|
|
entry.x = x;
|
|
entry.y = y;
|
|
entry.filename = filename;
|
|
entry.loop = loop;
|
|
|
|
if (!entry->loadFile(filename))
|
|
return NULL_VID_HANDLE;
|
|
|
|
// Search for any deleted videos so we can take a formerly used slot
|
|
for (uint32 i = 0; i < _videoStreams.size(); i++)
|
|
if (!_videoStreams[i].video) {
|
|
_videoStreams[i] = entry;
|
|
return i;
|
|
}
|
|
|
|
// Otherwise, just add it to the list
|
|
_videoStreams.push_back(entry);
|
|
return _videoStreams.size() - 1;
|
|
}
|
|
|
|
void VideoManager::seekToTime(VideoHandle handle, uint32 time) {
|
|
if (handle != NULL_VID_HANDLE)
|
|
_videoStreams[handle]->seekToTime(Audio::Timestamp(0, time, 600));
|
|
}
|
|
|
|
void VideoManager::copyFrameToScreen(const Graphics::Surface *frame, int width, int height, int x, int y) {
|
|
if (frame->format.bytesPerPixel == 1)
|
|
error("Unhandled 8bpp frames"); // Cut out because Pegasus Prime shouldn't need this
|
|
|
|
// Clip the width/height to make sure we stay on the screen
|
|
width = MIN<int32>(width, _vm->_system->getWidth() - x);
|
|
height = MIN<int32>(height, _vm->_system->getHeight() - y);
|
|
|
|
if (width == 320 && height == 240) {
|
|
// TODO: Is this right? At least "Big Movie" and the "Sub Chase Movie" need to be scaled...
|
|
// FIXME: Normal2x is only compiled in when USE_SCALERS is defined
|
|
Graphics::Surface scaledSurf;
|
|
scaledSurf.create(frame->w * 2, frame->h * 2, frame->format);
|
|
Normal2x((byte *)frame->pixels, frame->pitch, (byte *)scaledSurf.pixels, scaledSurf.pitch, frame->w, frame->h);
|
|
_vm->_system->copyRectToScreen((byte *)scaledSurf.pixels, scaledSurf.pitch, 0, 0, width * 2, height * 2);
|
|
scaledSurf.free();
|
|
} else
|
|
_vm->_system->copyRectToScreen((byte *)frame->pixels, frame->pitch, x, y, width, height);
|
|
}
|
|
|
|
} // End of namespace Pegasus
|