1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * Additional copyright for this file:
8 * Copyright (C) 1995-1997 Presto Studios, Inc.
9 *
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License
12 * as published by the Free Software Foundation; either version 2
13 * of the License, or (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 *
24 */
25
26 #include "common/system.h"
27 #include "graphics/surface.h"
28 #include "video/qt_decoder.h"
29 #include "video/video_decoder.h"
30
31 #include "pegasus/movie.h"
32
33 namespace Pegasus {
34
Movie(const DisplayElementID id)35 Movie::Movie(const DisplayElementID id) : Animation(id) {
36 _video = 0;
37 setScale(600);
38 }
39
~Movie()40 Movie::~Movie() {
41 releaseMovie();
42 }
43
44 // *** Make sure this will stop displaying the movie.
45
releaseMovie()46 void Movie::releaseMovie() {
47 if (_video) {
48 delete _video;
49 _video = 0;
50 disposeAllCallBacks();
51 deallocateSurface();
52 }
53
54 setBounds(Common::Rect(0, 0, 0, 0));
55 }
56
initFromMovieFile(const Common::String & fileName,bool transparent)57 void Movie::initFromMovieFile(const Common::String &fileName, bool transparent) {
58 _transparent = transparent;
59
60 releaseMovie();
61 _video = new Video::QuickTimeDecoder();
62 if (!_video->loadFile(fileName)) {
63 // Replace any colon with an underscore, since only Mac OS X
64 // supports that. See PegasusEngine::detectOpeningClosingDirectory()
65 // for more info.
66 Common::String newName(fileName);
67 if (newName.contains(':'))
68 for (uint i = 0; i < newName.size(); i++)
69 if (newName[i] == ':')
70 newName.setChar('_', i);
71
72 if (!_video->loadFile(newName))
73 error("Could not load video '%s'", fileName.c_str());
74 }
75
76 Common::Rect bounds(0, 0, _video->getWidth(), _video->getHeight());
77 sizeElement(_video->getWidth(), _video->getHeight());
78 _movieBox = bounds;
79
80 if (!isSurfaceValid())
81 allocateSurface(bounds);
82
83 setStart(0, getScale());
84 TimeBase::setStop(_video->getDuration().convertToFramerate(getScale()).totalNumberOfFrames(), getScale());
85 }
86
redrawMovieWorld()87 void Movie::redrawMovieWorld() {
88 if (_video && _video->needsUpdate()) {
89 const Graphics::Surface *frame = _video->decodeNextFrame();
90
91 if (!frame)
92 return;
93
94 // Make sure we have a surface in the current pixel format
95 Graphics::Surface *convertedFrame = 0;
96
97 if (frame->format != g_system->getScreenFormat()) {
98 convertedFrame = frame->convertTo(g_system->getScreenFormat());
99 frame = convertedFrame;
100 }
101
102 // Copy to the surface using _movieBox
103 uint16 width = MIN<int>(frame->w, _movieBox.width());
104 uint16 height = MIN<int>(frame->h, _movieBox.height());
105
106 for (uint16 y = 0; y < height; y++)
107 memcpy((byte *)_surface->getBasePtr(_movieBox.left, _movieBox.top + y), (const byte *)frame->getBasePtr(0, y), width * frame->format.bytesPerPixel);
108
109 if (convertedFrame) {
110 convertedFrame->free();
111 delete convertedFrame;
112 }
113
114 triggerRedraw();
115 }
116 }
117
draw(const Common::Rect & r)118 void Movie::draw(const Common::Rect &r) {
119 Common::Rect worldBounds = _movieBox;
120 Common::Rect elementBounds;
121 getBounds(elementBounds);
122
123 worldBounds.moveTo(elementBounds.left, elementBounds.top);
124 Common::Rect r1 = r.findIntersectingRect(worldBounds);
125
126 Common::Rect r2 = r1;
127 r2.translate(_movieBox.left - elementBounds.left, _movieBox.top - elementBounds.top);
128 drawImage(r2, r1);
129 }
130
moveMovieBoxTo(const CoordType h,const CoordType v)131 void Movie::moveMovieBoxTo(const CoordType h, const CoordType v) {
132 _movieBox.moveTo(h, v);
133 }
134
setStop(const TimeValue stopTime,const TimeScale scale)135 void Movie::setStop(const TimeValue stopTime, const TimeScale scale) {
136 TimeBase::setStop(stopTime, scale);
137
138 if (_video)
139 _video->setEndTime(Audio::Timestamp(0, _stopTime, _stopScale));
140 }
141
setVolume(uint16 volume)142 void Movie::setVolume(uint16 volume) {
143 if (_video)
144 _video->setVolume(MIN<uint>(volume, 0xFF));
145 }
146
setTime(const TimeValue time,const TimeScale scale)147 void Movie::setTime(const TimeValue time, const TimeScale scale) {
148 if (_video) {
149 // Don't go past the ends of the movie
150 Common::Rational timeFrac = Common::Rational(time, ((scale == 0) ? getScale() : scale));
151
152 if (timeFrac < Common::Rational(_startTime, _startScale))
153 timeFrac = Common::Rational(_startTime, _startScale);
154 else if (timeFrac >= Common::Rational(_stopTime, _stopScale))
155 return;
156
157 _video->seek(Audio::Timestamp(0, timeFrac.getNumerator(), timeFrac.getDenominator()));
158 _time = timeFrac;
159 _lastMillis = 0;
160 }
161 }
162
setRate(const Common::Rational rate)163 void Movie::setRate(const Common::Rational rate) {
164 if (_video) {
165 _video->setRate(rate);
166
167 TimeBase::setRate(_video->getRate());
168 return;
169 }
170
171 TimeBase::setRate(rate);
172 }
173
start()174 void Movie::start() {
175 if (_video)
176 _video->start();
177
178 TimeBase::start();
179 }
180
stop()181 void Movie::stop() {
182 if (_video)
183 _video->stop();
184
185 TimeBase::stop();
186 }
187
resume()188 void Movie::resume() {
189 if (_paused) {
190 if (_video)
191 _video->pauseVideo(false);
192
193 _paused = false;
194 }
195 }
196
pause()197 void Movie::pause() {
198 if (isRunning() && !_paused) {
199 if (_video)
200 _video->pauseVideo(true);
201
202 _paused = true;
203 _pauseStart = g_system->getMillis();
204 }
205 }
206
getDuration(const TimeScale scale) const207 TimeValue Movie::getDuration(const TimeScale scale) const {
208 // Unlike TimeBase::getDuration(), this returns the whole duration of the movie
209 // The original source has a TODO to make this behave like TimeBase::getDuration(),
210 // but the problem is that too much code requires this function to behave this way...
211
212 if (_video)
213 return _video->getDuration().convertToFramerate(((scale == 0) ? getScale() : scale)).totalNumberOfFrames();
214
215 return 0;
216 }
217
updateTime()218 void Movie::updateTime() {
219 // The reason why we overrode TimeBase's updateTime():
220 // Again, avoiding timers and handling it here
221 if (_video && _video->isPlaying() && !_video->isPaused()) {
222 redrawMovieWorld();
223
224 uint32 startTime = _startTime * getScale() / _startScale;
225 uint32 stopTime = _stopTime * getScale() / _stopScale;
226 uint32 actualTime = CLIP<int>(_video->getTime() * getScale() / 1000, startTime, stopTime);
227
228 // HACK: Due to the inaccuracy of the time, we won't actually allow us to hit
229 // the stop time unless we've actually reached the end of the segment.
230 if (actualTime == stopTime && !_video->endOfVideo())
231 actualTime--;
232
233 _time = Common::Rational(actualTime, getScale());
234 }
235 }
236
GlowingMovie(const DisplayElementID id)237 GlowingMovie::GlowingMovie(const DisplayElementID id) : Movie(id) {
238 _glowing = false;
239 }
240
draw(const Common::Rect & r)241 void GlowingMovie::draw(const Common::Rect &r) {
242 // Make sure the rectangles are clipped properly, OR guarantee that _bounds will
243 // never fall off the screen.
244 if (_glowing) {
245 Common::Rect bounds;
246 getBounds(bounds);
247
248 copyToCurrentPortTransparentGlow(_movieBox, bounds);
249 } else {
250 Movie::draw(r);
251 }
252 }
253
setBounds(const Common::Rect & r)254 void GlowingMovie::setBounds(const Common::Rect &r) {
255 Common::Rect bounds;
256 getBounds(bounds);
257
258 if (r != bounds) {
259 // Avoid Movie::setBounds.
260 // clone2727 asks why, but goes along with it
261 Animation::setBounds(r);
262 }
263 }
264
ScalingMovie(const DisplayElementID id)265 ScalingMovie::ScalingMovie(const DisplayElementID id) : GlowingMovie(id) {
266 }
267
draw(const Common::Rect &)268 void ScalingMovie::draw(const Common::Rect &) {
269 // Make sure the rectangles are clipped properly, OR guarantee that _bounds will
270 // never fall off the screen.
271
272 Common::Rect bounds;
273 getBounds(bounds);
274
275 if (_glowing)
276 scaleTransparentCopyGlow(_movieBox, bounds);
277 else
278 scaleTransparentCopy(_movieBox, bounds);
279 }
280
281 } // End of namespace Pegasus
282