2018-12-05 01:02:27 +02:00

745 lines
18 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.
*
*/
#ifndef SCI_GRAPHICS_VIDEO32_H
#define SCI_GRAPHICS_VIDEO32_H
#ifdef USE_RGB_COLOR
#include "common/config-manager.h" // for ConfMan
#endif
#include "common/ptr.h"
#include "common/rect.h" // for Rect
#include "common/scummsys.h" // for int16, uint8, uint16, int32
#include "common/str.h" // for String
#include "sci/engine/vm_types.h" // for reg_t
#include "sci/video/robot_decoder.h" // for RobotDecoder
#include "sci/sound/audio32.h" // for Audio32::kMaxVolume
#include "video/avi_decoder.h" // for AVIDecoder::setVolume
namespace Video {
class AdvancedVMDDecoder;
}
namespace Sci {
class EventManager;
class Plane;
class ScreenItem;
class SegManager;
class SEQDecoder;
struct Palette;
/**
* An abstract class implementing common video playback functionality for SCI
* engine.
*/
class VideoPlayer {
public:
enum EventFlags {
kEventFlagNone = 0,
kEventFlagEnd = 1,
kEventFlagEscapeKey = 2,
kEventFlagMouseDown = 4,
kEventFlagHotRectangle = 8,
kEventFlagToFrame = 0x10,
kEventFlagYieldToVM = 0x20,
kEventFlagReverse = 0x80
};
friend EventFlags operator|(const EventFlags a, const EventFlags b) {
return static_cast<EventFlags>((int)a | (int)b);
}
VideoPlayer(EventManager *eventMan, Video::VideoDecoder *decoder = nullptr) :
_eventMan(eventMan),
_decoder(decoder)
#ifdef USE_RGB_COLOR
,
_hqVideoMode(false)
#endif
{}
virtual ~VideoPlayer() {}
protected:
EventManager *_eventMan;
/**
* The video decoder to use for video playback by this player.
*/
Common::ScopedPtr<Video::VideoDecoder> _decoder;
/**
* Attempts to open a video by filename and performs basic validation to
* ensure that the current system is actually capable of playing back the
* video.
*/
bool open(const Common::String &fileName);
/**
* Reinitializes the system hardware surface for playback of high-quality
* scaled video if the current video meets the necessary criteria for this
* playback mode.
*
* @returns whether or not the system surface was reinitialized for
* high-quality scaled video.
*/
bool startHQVideo();
/**
* Determines whether or not the currently loaded video meets the criteria
* for high-quality scaled output.
*/
virtual bool shouldStartHQVideo() const {
#ifdef USE_RGB_COLOR
if (!ConfMan.getBool("enable_hq_video")) {
return false;
}
if (_decoder->getWidth() == _drawRect.width() &&
_decoder->getHeight() == _drawRect.height()) {
return false;
}
return true;
#else
return false;
#endif
}
/**
* Restores the hardware surface back to CLUT8 after video playback.
*/
bool endHQVideo();
/**
* Plays a video until an event in the given `flags` is encountered, or
* until the end of the video is reached.
*
* @param maxSleepMs An optional parameter defining the maximum number of
* milliseconds that the video player should sleep between video frames.
*/
virtual EventFlags playUntilEvent(const EventFlags flags, const uint32 maxSleepMs = 0xFFFFFFFF);
/**
* Checks to see if an event has occurred that should cause the video player
* to yield back to the VM.
*/
virtual EventFlags checkForEvent(const EventFlags flags);
/**
* Submits a palette from the video to the system.
*/
virtual void submitPalette(const uint8 palette[256 * 3]) const;
/**
* Renders a video frame to the system.
*/
virtual void renderFrame(const Graphics::Surface &nextFrame) const;
/**
* Renders a video frame to an intermediate surface using low-quality
* scaling, black-lining, or direct copy, depending upon the passed flags.
*/
template <typename PixelType>
void renderLQToSurface(Graphics::Surface &out, const Graphics::Surface &nextFrame, const bool doublePixels, const bool blackLines) const;
/**
* Sets the draw rect, clipping it to the screen's dimensions if necessary.
*/
void setDrawRect(const int16 x, const int16 y, const int16 width, const int16 height);
/**
* The rectangle where the video will be drawn, in screen coordinates.
*/
Common::Rect _drawRect;
#ifdef USE_RGB_COLOR
/**
* Whether or not the player is currently in high-quality video rendering
* mode.
*/
bool _hqVideoMode;
#endif
};
#pragma mark SEQPlayer
/**
* SEQPlayer is used to play SEQ animations.
* Used by DOS versions of GK1 and QFG4CD.
*/
class SEQPlayer : public VideoPlayer {
public:
SEQPlayer(EventManager *eventMan);
/**
* Plays a SEQ animation with the given file name, with each frame being
* displayed for `numTicks` ticks.
*/
void play(const Common::String &fileName, const int16 numTicks, const int16 x, const int16 y);
};
#pragma mark -
#pragma mark AVIPlayer
/**
* AVIPlayer is used to play AVI videos.
* Used by Windows versions of GK1CD, KQ7, and QFG4CD.
*/
class AVIPlayer : public VideoPlayer {
public:
enum IOStatus {
kIOSuccess = 0,
kIOFileNotFound = 2,
kIOSeekFailed = 12
};
enum AVIStatus {
kAVINotOpen = 0,
kAVIOpen = 1,
kAVIPlaying = 2,
kAVIPaused = 3
};
AVIPlayer(EventManager *eventMan);
/**
* Opens a stream to an AVI resource.
*/
IOStatus open(const Common::String &fileName);
/**
* Initializes the AVI rendering parameters for the current AVI. This must
* be called after `open`.
*/
IOStatus init(const bool doublePixels);
/**
* Begins playback of the current AVI.
*/
IOStatus play(const int16 from, const int16 to, const int16 showStyle, const bool cue);
virtual EventFlags playUntilEvent(const EventFlags flags, const uint32 maxSleepMs = 0xFFFFFFFF) override;
/**
* Stops playback and closes the currently open AVI stream.
*/
IOStatus close();
/**
* Seeks the currently open AVI stream to the given frame.
*/
IOStatus cue(const uint16 frameNo);
/**
* Returns the duration of the current video.
*/
uint16 getDuration() const;
private:
/**
* Playback status of the player.
*/
AVIStatus _status;
};
#pragma mark -
#pragma mark VMDPlayer
/**
* VMDPlayer is used to play VMD videos.
* Used by LSL7, Phant1, GK2, PQ:SWAT, Shivers, SQ6, Rama, Torin, and
* Lighthouse.
*/
class VMDPlayer : public VideoPlayer {
public:
enum OpenFlags {
kOpenFlagNone = 0,
kOpenFlagMute = 1
};
enum IOStatus {
kIOSuccess = 0,
kIOError = 0xFFFF
};
enum PlayFlags {
kPlayFlagNone = 0,
kPlayFlagDoublePixels = 1,
kPlayFlagBlackLines = 4,
kPlayFlagBoost = 0x10,
kPlayFlagLeaveScreenBlack = 0x20,
kPlayFlagLeaveLastFrame = 0x40,
kPlayFlagBlackPalette = 0x80,
kPlayFlagStretchVertical = 0x100
};
enum VMDStatus {
kVMDNotOpen = 0,
kVMDOpen = 1,
kVMDPlaying = 2,
kVMDPaused = 3,
kVMDStopped = 4,
kVMDFinished = 5
};
VMDPlayer(EventManager *eventMan, SegManager *segMan);
virtual ~VMDPlayer();
private:
SegManager *_segMan;
#pragma mark -
#pragma mark VMDPlayer - Playback
public:
/**
* Opens a stream to a VMD resource.
*/
IOStatus open(const Common::String &fileName, const OpenFlags flags);
/**
* Initializes the VMD rendering parameters for the current VMD. This must
* be called after `open`.
*/
void init(int16 x, int16 y, const PlayFlags flags, const int16 boostPercent, const int16 boostStartColor, const int16 boostEndColor);
/**
* Stops playback and closes the currently open VMD stream.
*/
IOStatus close();
/**
* Gets the playback status of the VMD player.
*/
VMDStatus getStatus() const;
// Was WaitForEvent in SSCI
EventFlags kernelPlayUntilEvent(const EventFlags flags, const int16 lastFrameNo, const int16 yieldInterval);
private:
/**
* Whether or not a VMD stream has been opened with `open`.
*/
bool _isOpen;
/**
* Whether or not a VMD player has been initialized with `init`.
*/
bool _isInitialized;
/**
* The Resource object for VMDs that are read out of a resource bundle
* instead of being streamed from the filesystem. The resource is owned by
* ResourceManager.
*/
Resource *_bundledVmd;
/**
* For VMDs played with the `kEventFlagToFrame` flag, the target frame for
* yielding back to the SCI VM.
*/
int32 _yieldFrame;
/**
* For VMDs played with the `kEventFlagYieldToVM` flag, the number of frames
* that should be rendered until yielding back to the SCI VM.
*/
int32 _yieldInterval;
/**
* For VMDs played with the `kEventFlagYieldToVM` flag, the last frame when
* control of the main thread was yielded back to the SCI VM.
*/
int _lastYieldedFrameNo;
virtual EventFlags playUntilEvent(const EventFlags flags, const uint32 = 0xFFFFFFFF) override;
virtual EventFlags checkForEvent(const EventFlags flags) override;
#pragma mark -
#pragma mark VMDPlayer - Rendering
public:
/**
* Causes the VMD player to ignore all palettes in the currently playing
* video.
*/
void ignorePalettes() { _ignorePalettes = true; }
/**
* Sets the plane and plane priority used to render video.
*/
void setPlane(const int16 priority, const reg_t planeId);
protected:
/**
* Renders a frame of video to the output bitmap.
*/
virtual void renderFrame(const Graphics::Surface &nextFrame) const override;
/**
* Updates the system with palette data from the video.
*/
virtual void submitPalette(const uint8 palette[256 * 3]) const override;
private:
/**
* The plane where the VMD will be drawn. The plane is owned by GfxFrameout.
*/
Plane *_plane;
/**
* The screen item representing the VMD surface. The screen item is owned by
* GfxFrameout.
*/
ScreenItem *_screenItem;
/**
* The bitmap used to render the VMD.
*/
reg_t _bitmapId;
/**
* If true, the plane for this VMD was set externally and is not owned by
* this VMDPlayer.
*/
bool _planeIsOwned;
/**
* The screen priority of the video.
* @see ScreenItem::_priority
*/
int _priority;
/**
* Whether or not the video should be pixel doubled.
*/
bool _doublePixels;
/**
* Whether or not the video should be pixel doubled vertically only.
*/
bool _stretchVertical;
/**
* Whether or not black lines should be rendered across the video.
*/
bool _blackLines;
/**
* Whether or not the playback area of the VMD should be left black at the
* end of playback.
*/
bool _leaveScreenBlack;
/**
* Whether or not the area of the VMD should be left displaying the final
* frame of the video.
*/
bool _leaveLastFrame;
/**
* Whether or not palettes from the VMD should be ignored.
*/
bool _ignorePalettes;
/**
* Whether or not rendering mode is composited.
*/
bool _isComposited;
/**
* Fills the given palette with RGB values from the VMD palette, applying
* brightness boost if it is enabled.
*/
void fillPalette(const uint8 rawPalette[256 * 3], Palette &outPalette) const;
#ifdef USE_RGB_COLOR
/**
* Redraws areas of the screen outside of the video to the system buffer.
* This is used whenever palette changes occur and the video is rendering in
* high color mode.
*/
void redrawGameScreen() const;
/**
* Determines whether or not the VMD player should upgrade the renderer to
* high color depth when rendering the video.
*
* @TODO It should be possible in the future to allow high color composited
* video, but this will require additional work in GfxFrameout and
* GfxCursor32 since the internal buffer and cursor code are 8bpp only.
*/
virtual bool shouldStartHQVideo() const override {
if (!VideoPlayer::shouldStartHQVideo()) {
return false;
}
if (_priority != 0 || _leaveLastFrame || _showCursor || _blackLines) {
return false;
}
return true;
}
#endif
/**
* Determines whether or not the video should use the compositing renderer
* instead of the overlay renderer.
*/
bool shouldUseCompositing() const {
#ifdef USE_RGB_COLOR
return isNormallyComposited() && !shouldStartHQVideo();
#else
return isNormallyComposited();
#endif
}
bool isNormallyComposited() const {
return getSciVersion() == SCI_VERSION_3;
}
void initOverlay();
void renderOverlay(const Graphics::Surface &nextFrame) const;
void closeOverlay();
void initComposited();
void renderComposited() const;
void closeComposited();
#pragma mark -
#pragma mark VMDPlayer - Blackout
public:
/**
* Sets the area of the screen that should be blacked out during VMD
* playback.
*/
void setBlackoutArea(const Common::Rect &rect) { _blackoutRect = rect; }
private:
/**
* The dimensions of the blackout plane.
*/
Common::Rect _blackoutRect;
/**
* An optional plane that will be used to black out areas of the screen
* outside of the VMD surface. The plane is owned by GfxFrameout.
*/
Plane *_blackoutPlane;
#pragma mark -
#pragma mark VMDPlayer - Palette
public:
/**
* Restricts use of the system palette by VMD playback to the given range of
* palette indexes.
*/
void restrictPalette(const uint8 startColor, const int16 endColor);
private:
/**
* The first color in the system palette that the VMD can write to.
*/
uint8 _startColor;
/**
* The last color in the system palette that the VMD can write to.
*/
uint8 _endColor;
/**
* If true, video frames are rendered after a blank palette is submitted to
* the palette manager, which is then restored after the video pixels have
* already been rendered.
*
* This functionality is currently disabled because it seems like it was
* designed for a different graphics architecture where pixel data could be
* rendered before the video card's palette had been updated. This is not
* possible in ScummVM because the palette & pixel data are rendered
* simultaneously when OSystem::updateScreen is called, rather than
* immediately after they are sent to the backend.
*/
#ifdef SCI_VMD_BLACK_PALETTE
bool _blackPalette;
#endif
#pragma mark -
#pragma mark VMDPlayer - Brightness boost
private:
/**
* The amount of brightness boost for the video. Values above 100 increase
* brightness; values below 100 reduce it.
*/
int16 _boostPercent;
/**
* The first color in the palette that should be brightness boosted.
*/
uint8 _boostStartColor;
/**
* The last color in the palette that should be brightness boosted.
*/
uint8 _boostEndColor;
#pragma mark -
#pragma mark VMDPlayer - Mouse cursor
public:
/**
* Sets whether or not the mouse cursor should be drawn. This does not have
* any effect during playback, but can be used to prevent the mouse cursor
* from being shown again after the video has finished.
*/
void setShowCursor(const bool shouldShow) { _showCursor = shouldShow; }
private:
/**
* Whether or not the mouse cursor should be shown during playback.
*/
bool _showCursor;
};
#pragma mark -
#pragma mark DuckPlayer
/**
* DuckPlayer is used to play Duck TrueMotion videos.
* Used by Phantasmagoria 2.
*/
class DuckPlayer : public VideoPlayer {
public:
enum DuckStatus {
kDuckClosed = 0,
kDuckOpen = 1,
kDuckPlaying = 2,
kDuckPaused = 3
};
DuckPlayer(EventManager *eventMan, SegManager *segMan);
/**
* Opens a stream to a Duck resource.
*/
void open(const GuiResourceId resourceId, const int displayMode, const int16 x, const int16 y);
/**
* Stops playback and closes the currently open Duck stream.
*/
void close();
/**
* Begins playback of the current Duck video.
*/
void play(const int lastFrameNo);
/**
* Sets a flag indicating that an opaque plane should be added to the
* graphics manager underneath the video surface during playback.
*/
void setDoFrameOut(const bool value) { _doFrameOut = value; }
/**
* Sets the volume of the decoder.
*/
void setVolume(const uint8 value) {
_volume = value * Audio::Mixer::kMaxChannelVolume / Audio32::kMaxVolume;
_decoder->setVolume(_volume);
}
protected:
virtual bool shouldStartHQVideo() const override {
if (!VideoPlayer::shouldStartHQVideo() || _blackLines) {
return false;
}
return true;
}
virtual void renderFrame(const Graphics::Surface &nextFrame) const override;
private:
/**
* An empty plane drawn behind the video when the doFrameOut flag is true.
* The plane is owned by GfxFrameout.
*/
Plane *_plane;
/**
* The player status.
*/
DuckStatus _status;
/**
* The playback volume for the player.
*/
uint8 _volume;
/**
* If true, frameOut will be called during Duck video playback to update
* other parts of the screen.
*/
bool _doFrameOut;
/**
* Whether or not the video should be pixel doubled.
*/
bool _doublePixels;
/**
* Whether or not black lines should be rendered across the video.
*/
bool _blackLines;
};
#pragma mark -
#pragma mark Video32
/**
* Video32 provides facilities for playing back video in SCI engine.
*/
class Video32 : public Common::Serializable {
public:
Video32(SegManager *segMan, EventManager *eventMan) :
_SEQPlayer(eventMan),
_AVIPlayer(eventMan),
_VMDPlayer(eventMan, segMan),
_robotPlayer(segMan),
_duckPlayer(eventMan, segMan) {}
void beforeSaveLoadWithSerializer(Common::Serializer &ser);
virtual void saveLoadWithSerializer(Common::Serializer &ser);
SEQPlayer &getSEQPlayer() { return _SEQPlayer; }
AVIPlayer &getAVIPlayer() { return _AVIPlayer; }
VMDPlayer &getVMDPlayer() { return _VMDPlayer; }
RobotDecoder &getRobotPlayer() { return _robotPlayer; }
DuckPlayer &getDuckPlayer() { return _duckPlayer; }
private:
SEQPlayer _SEQPlayer;
AVIPlayer _AVIPlayer;
VMDPlayer _VMDPlayer;
RobotDecoder _robotPlayer;
DuckPlayer _duckPlayer;
};
} // End of namespace Sci
#endif