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  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #include "common/scummsys.h"
24 
25 #include "zvision/scripting/effects/animation_effect.h"
26 
27 #include "zvision/zvision.h"
28 #include "zvision/graphics/render_manager.h"
29 #include "zvision/scripting/script_manager.h"
30 
31 #include "graphics/surface.h"
32 #include "video/video_decoder.h"
33 
34 namespace ZVision {
35 
AnimationEffect(ZVision * engine,uint32 controlKey,const Common::String & fileName,int32 mask,int32 frate,bool disposeAfterUse)36 AnimationEffect::AnimationEffect(ZVision *engine, uint32 controlKey, const Common::String &fileName, int32 mask, int32 frate, bool disposeAfterUse)
37 	: ScriptingEffect(engine, controlKey, SCRIPTING_EFFECT_ANIM),
38 	  _disposeAfterUse(disposeAfterUse),
39 	  _mask(mask),
40 	  _animation(NULL) {
41 
42 	_animation = engine->loadAnimation(fileName);
43 
44 	if (frate > 0) {
45 		_frmDelayOverride = (int32)(1000.0 / frate);
46 
47 		// WORKAROUND: We do not allow the engine to delay more than 66 msec
48 		// per frame (15fps max)
49 		if (_frmDelayOverride > 66)
50 			_frmDelayOverride = 66;
51 	} else {
52 		_frmDelayOverride = 0;
53 	}
54 }
55 
~AnimationEffect()56 AnimationEffect::~AnimationEffect() {
57 	if (_animation)
58 		delete _animation;
59 
60 	_engine->getScriptManager()->setStateValue(_key, 2);
61 
62 	PlayNodes::iterator it = _playList.begin();
63 	if (it != _playList.end()) {
64 		_engine->getScriptManager()->setStateValue((*it).slot, 2);
65 
66 		if ((*it)._scaled) {
67 			(*it)._scaled->free();
68 			delete(*it)._scaled;
69 		}
70 	}
71 
72 	_playList.clear();
73 }
74 
process(uint32 deltaTimeInMillis)75 bool AnimationEffect::process(uint32 deltaTimeInMillis) {
76 	ScriptManager *scriptManager = _engine->getScriptManager();
77 	RenderManager *renderManager = _engine->getRenderManager();
78 	RenderTable::RenderState renderState = renderManager->getRenderTable()->getRenderState();
79 	bool isPanorama = (renderState == RenderTable::PANORAMA);
80 	int16 velocity = _engine->getMouseVelocity() + _engine->getKeyboardVelocity();
81 
82 	// Do not update animation nodes in panoramic mode while turning, if the user
83 	// has set this option
84 	if (scriptManager->getStateValue(StateKey_NoTurnAnim) == 1 && isPanorama && velocity)
85 		return false;
86 
87 	PlayNodes::iterator it = _playList.begin();
88 	if (it != _playList.end()) {
89 		playnode *nod = &(*it);
90 
91 		if (nod->_curFrame == -1) {
92 			// The node is just beginning playback
93 			nod->_curFrame = nod->start;
94 
95 			_animation->start();
96 			_animation->seekToFrame(nod->start);
97 			_animation->setEndFrame(nod->stop);
98 
99 			nod->_delay = deltaTimeInMillis; // Force the frame to draw
100 			if (nod->slot)
101 				scriptManager->setStateValue(nod->slot, 1);
102 		} else if (_animation->endOfVideo()) {
103 			// The node has reached the end; check if we need to loop
104 			nod->loop--;
105 
106 			if (nod->loop == 0) {
107 				if (nod->slot >= 0)
108 					scriptManager->setStateValue(nod->slot, 2);
109 				if (nod->_scaled) {
110 					nod->_scaled->free();
111 					delete nod->_scaled;
112 				}
113 				_playList.erase(it);
114 				return _disposeAfterUse;
115 			}
116 
117 			nod->_curFrame = nod->start;
118 			_animation->seekToFrame(nod->start);
119 		}
120 
121 		// Check if we need to draw a frame
122 		bool needsUpdate = false;
123 		if (_frmDelayOverride == 0) {
124 			// If not overridden, use the VideoDecoder's check
125 			needsUpdate = _animation->needsUpdate();
126 		} else {
127 			// Otherwise, implement our own timing
128 			nod->_delay -= deltaTimeInMillis;
129 
130 			if (nod->_delay <= 0) {
131 				nod->_delay += _frmDelayOverride;
132 				needsUpdate = true;
133 			}
134 		}
135 
136 		if (needsUpdate) {
137 			const Graphics::Surface *frame = _animation->decodeNextFrame();
138 
139 			if (frame) {
140 				uint32 dstw;
141 				uint32 dsth;
142 				if (isPanorama) {
143 					dstw = nod->pos.height();
144 					dsth = nod->pos.width();
145 				} else {
146 					dstw = nod->pos.width();
147 					dsth = nod->pos.height();
148 				}
149 
150 				// We only scale down the animation to fit its frame, not up, otherwise we
151 				// end up with distorted animations - e.g. the armor visor in location cz1e
152 				// in Nemesis (one of the armors inside Irondune), or the planet in location
153 				// aa10 in Nemesis (Juperon, outside the asylum). We do allow scaling up only
154 				// when a simple 2x filter is requested (e.g. the alchemists and cup sequence
155 				// in Nemesis)
156 				if (frame->w > dstw || frame->h > dsth || (frame->w == dstw / 2 && frame->h == dsth / 2)) {
157 					if (nod->_scaled)
158 						if (nod->_scaled->w != dstw || nod->_scaled->h != dsth) {
159 							nod->_scaled->free();
160 							delete nod->_scaled;
161 							nod->_scaled = NULL;
162 						}
163 
164 					if (!nod->_scaled) {
165 						nod->_scaled = new Graphics::Surface;
166 						nod->_scaled->create(dstw, dsth, frame->format);
167 					}
168 
169 					renderManager->scaleBuffer(frame->getPixels(), nod->_scaled->getPixels(), frame->w, frame->h, frame->format.bytesPerPixel, dstw, dsth);
170 					frame = nod->_scaled;
171 				}
172 
173 				if (isPanorama) {
174 					Graphics::Surface *transposed = RenderManager::tranposeSurface(frame);
175 					renderManager->blitSurfaceToBkg(*transposed, nod->pos.left, nod->pos.top, _mask);
176 					transposed->free();
177 					delete transposed;
178 				} else {
179 					renderManager->blitSurfaceToBkg(*frame, nod->pos.left, nod->pos.top, _mask);
180 				}
181 			}
182 		}
183 	}
184 
185 	return false;
186 }
187 
addPlayNode(int32 slot,int x,int y,int x2,int y2,int startFrame,int endFrame,int loops)188 void AnimationEffect::addPlayNode(int32 slot, int x, int y, int x2, int y2, int startFrame, int endFrame, int loops) {
189 	playnode nod;
190 	nod.loop = loops;
191 	nod.pos = Common::Rect(x, y, x2 + 1, y2 + 1);
192 	nod.start = startFrame;
193 	nod.stop = CLIP<int>(endFrame, 0, _animation->getFrameCount() - 1);
194 	nod.slot = slot;
195 	nod._curFrame = -1;
196 	nod._delay = 0;
197 	nod._scaled = NULL;
198 	_playList.push_back(nod);
199 }
200 
stop()201 bool AnimationEffect::stop() {
202 	PlayNodes::iterator it = _playList.begin();
203 	if (it != _playList.end()) {
204 		_engine->getScriptManager()->setStateValue((*it).slot, 2);
205 		if ((*it)._scaled) {
206 			(*it)._scaled->free();
207 			delete(*it)._scaled;
208 		}
209 	}
210 
211 	_playList.clear();
212 
213 	// We don't need to delete, it's may be reused
214 	return false;
215 }
216 
217 } // End of namespace ZVision
218