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/macresman.h"
27 #include "common/stream.h"
28 
29 #include "pegasus/elements.h"
30 #include "pegasus/graphics.h"
31 #include "pegasus/surface.h"
32 
33 namespace Pegasus {
34 
DisplayElement(const DisplayElementID id)35 DisplayElement::DisplayElement(const DisplayElementID id) : IDObject(id) {
36 	_elementIsDisplaying = false;
37 	_elementIsVisible = false;
38 	_elementOrder = 0;
39 	_triggeredElement = this;
40 	_nextElement = 0;
41 }
42 
~DisplayElement()43 DisplayElement::~DisplayElement() {
44 	if (isDisplaying())
45 		((PegasusEngine *)g_engine)->_gfx->removeDisplayElement(this);
46 }
47 
setDisplayOrder(const DisplayOrder order)48 void DisplayElement::setDisplayOrder(const DisplayOrder order) {
49 	if (_elementOrder != order) {
50 		_elementOrder = order;
51 		if (isDisplaying()) {
52 			((PegasusEngine *)g_engine)->_gfx->removeDisplayElement(this);
53 			((PegasusEngine *)g_engine)->_gfx->addDisplayElement(this);
54 			triggerRedraw();
55 		}
56 	}
57 }
58 
startDisplaying()59 void DisplayElement::startDisplaying() {
60 	if (!isDisplaying()) {
61 		((PegasusEngine *)g_engine)->_gfx->addDisplayElement(this);
62 		triggerRedraw();
63 	}
64 }
65 
stopDisplaying()66 void DisplayElement::stopDisplaying() {
67 	if (isDisplaying()) {
68 		triggerRedraw();
69 		((PegasusEngine *)g_engine)->_gfx->removeDisplayElement(this);
70 	}
71 }
72 
setBounds(const CoordType left,const CoordType top,const CoordType right,const CoordType bottom)73 void DisplayElement::setBounds(const CoordType left, const CoordType top, const CoordType right, const CoordType bottom) {
74 	setBounds(Common::Rect(left, top, right, bottom));
75 }
76 
getBounds(Common::Rect & r) const77 void DisplayElement::getBounds(Common::Rect &r) const {
78 	r = _bounds;
79 }
80 
sizeElement(const CoordType h,const CoordType v)81 void DisplayElement::sizeElement(const CoordType h, const CoordType v) {
82 	Common::Rect newBounds = _bounds;
83 	newBounds.right = _bounds.left + h;
84 	newBounds.bottom = _bounds.top + v;
85 	setBounds(newBounds);
86 }
87 
moveElementTo(const CoordType h,const CoordType v)88 void DisplayElement::moveElementTo(const CoordType h, const CoordType v) {
89 	Common::Rect newBounds = _bounds;
90 	newBounds.moveTo(h, v);
91 	setBounds(newBounds);
92 }
93 
moveElement(const CoordType dh,const CoordType dv)94 void DisplayElement::moveElement(const CoordType dh, const CoordType dv) {
95 	Common::Rect newBounds = _bounds;
96 	newBounds.translate(dh, dv);
97 	setBounds(newBounds);
98 }
99 
getLocation(CoordType & h,CoordType & v) const100 void DisplayElement::getLocation(CoordType &h, CoordType &v) const {
101 	h = _bounds.left;
102 	v = _bounds.top;
103 }
104 
centerElementAt(const CoordType h,const CoordType v)105 void DisplayElement::centerElementAt(const CoordType h, const CoordType v) {
106 	Common::Rect newBounds = _bounds;
107 	newBounds.moveTo(h - (_bounds.width() / 2), v - (_bounds.height() / 2));
108 	setBounds(newBounds);
109 }
110 
getCenter(CoordType & h,CoordType & v) const111 void DisplayElement::getCenter(CoordType &h, CoordType &v) const {
112 	h = (_bounds.left + _bounds.right) / 2;
113 	v = (_bounds.top + _bounds.bottom) / 2;
114 }
115 
setBounds(const Common::Rect & r)116 void DisplayElement::setBounds(const Common::Rect &r) {
117 	if (r != _bounds) {
118 		triggerRedraw();
119 		_bounds = r;
120 		triggerRedraw();
121 	}
122 }
123 
hide()124 void DisplayElement::hide() {
125 	if (_elementIsVisible) {
126 		triggerRedraw();
127 		_elementIsVisible = false;
128 	}
129 }
130 
show()131 void DisplayElement::show() {
132 	if (!_elementIsVisible) {
133 		_elementIsVisible = true;
134 		triggerRedraw();
135 	}
136 }
137 
138 // Only invalidates this element's bounding rectangle if all these conditions are true:
139 // -- The triggered element is this element.
140 // -- The element is displaying on the display list.
141 // -- The element is visible.
142 // -- The element is part of the active layer OR is one of the reserved items.
triggerRedraw()143 void DisplayElement::triggerRedraw() {
144 	GraphicsManager *gfx = ((PegasusEngine *)g_engine)->_gfx;
145 
146 	if (_triggeredElement == this) {
147 		if (validToDraw(gfx->getBackOfActiveLayer(), gfx->getFrontOfActiveLayer()))
148 			gfx->invalRect(_bounds);
149 	} else {
150 		_triggeredElement->triggerRedraw();
151 	}
152 }
153 
setTriggeredElement(DisplayElement * element)154 void DisplayElement::setTriggeredElement(DisplayElement *element) {
155 	if (element)
156 		_triggeredElement = element;
157 	else
158 		_triggeredElement = this;
159 }
160 
validToDraw(DisplayOrder backLayer,DisplayOrder frontLayer)161 bool DisplayElement::validToDraw(DisplayOrder backLayer, DisplayOrder frontLayer) {
162 	return	isDisplaying() && _elementIsVisible &&
163 			(getObjectID() <= kHighestReservedElementID ||
164 			(getDisplayOrder() >= backLayer &&
165 			getDisplayOrder() <= frontLayer));
166 }
167 
DropHighlight(const DisplayElementID id)168 DropHighlight::DropHighlight(const DisplayElementID id) : DisplayElement(id) {
169 	_highlightColor = 0;
170 	_thickness = 2;
171 	_cornerDiameter = 0;
172 }
173 
draw(const Common::Rect &)174 void DropHighlight::draw(const Common::Rect &) {
175 	Graphics::Surface *screen = ((PegasusEngine *)g_engine)->_gfx->getWorkArea();
176 
177 	// Since this is only used in two different ways, I'm only
178 	// going to implement it in those two ways. Deal with it.
179 
180 	Common::Rect rect = _bounds;
181 	rect.grow(-_thickness);
182 	screen->frameRect(rect, _highlightColor);
183 	rect.grow(1);
184 	screen->frameRect(rect, _highlightColor);
185 
186 	if (_cornerDiameter == 8 && _thickness == 4) {
187 		rect.grow(1);
188 		screen->frameRect(rect, _highlightColor);
189 		screen->hLine(rect.left + 1, rect.top - 1, rect.right - 2, _highlightColor);
190 		screen->hLine(rect.left + 1, rect.bottom, rect.right - 2, _highlightColor);
191 		screen->vLine(rect.left - 1, rect.top + 1, rect.bottom - 2, _highlightColor);
192 		screen->vLine(rect.right, rect.top + 1, rect.bottom - 2, _highlightColor);
193 	}
194 }
195 
IdlerAnimation(const DisplayElementID id)196 IdlerAnimation::IdlerAnimation(const DisplayElementID id) : Animation(id) {
197 	_lastTime = 0xffffffff;
198 }
199 
startDisplaying()200 void IdlerAnimation::startDisplaying() {
201 	if (!isDisplaying()) {
202 		Animation::startDisplaying();
203 		startIdling();
204 	}
205 }
206 
stopDisplaying()207 void IdlerAnimation::stopDisplaying() {
208 	if (isDisplaying()) {
209 		Animation::stopDisplaying();
210 		stopIdling();
211 	}
212 }
213 
useIdleTime()214 void IdlerAnimation::useIdleTime() {
215 	uint32 currentTime = getTime();
216 
217 	if (currentTime != _lastTime) {
218 		_lastTime = currentTime;
219 		timeChanged(_lastTime);
220 	}
221 }
222 
timeChanged(const TimeValue)223 void IdlerAnimation::timeChanged(const TimeValue) {
224 	triggerRedraw();
225 }
226 
FrameSequence(const DisplayElementID id)227 FrameSequence::FrameSequence(const DisplayElementID id) : IdlerAnimation(id) {
228 	_duration = 0;
229 	_currentFrameNum = 0;
230 	_resFork = new Common::MacResManager();
231 	_numFrames = 0;
232 }
233 
~FrameSequence()234 FrameSequence::~FrameSequence() {
235 	delete _resFork;
236 }
237 
useFileName(const Common::String & fileName)238 void FrameSequence::useFileName(const Common::String &fileName) {
239 	_resFork->open(fileName);
240 }
241 
openFrameSequence()242 void FrameSequence::openFrameSequence() {
243 	if (!_resFork->hasResFork())
244 		return;
245 
246 	Common::SeekableReadStream *res = _resFork->getResource(MKTAG('P', 'F', 'r', 'm'), 0x80);
247 
248 	if (!res)
249 		return;
250 
251 	uint32 scale = res->readUint32BE();
252 	_bounds.top = res->readUint16BE();
253 	_bounds.left = res->readUint16BE();
254 	_bounds.bottom = res->readUint16BE();
255 	_bounds.right = res->readUint16BE();
256 	_numFrames = res->readUint16BE();
257 	_duration = 0;
258 
259 	_frameTimes.clear();
260 	for (uint32 i = 0; i < _numFrames; i++) {
261 		TimeValue time = res->readUint32BE();
262 		_frameTimes.push_back(_duration);
263 		_duration += time;
264 	}
265 
266 	setScale(scale);
267 	setSegment(0, _duration);
268 	setTime(0);
269 	_currentFrameNum = 0;
270 	newFrame(_currentFrameNum);
271 	triggerRedraw();
272 
273 	delete res;
274 }
275 
closeFrameSequence()276 void FrameSequence::closeFrameSequence() {
277 	stop();
278 	_resFork->close();
279 	_duration = 0;
280 	_numFrames = 0;
281 	_frameTimes.clear();
282 }
283 
timeChanged(const TimeValue time)284 void FrameSequence::timeChanged(const TimeValue time) {
285 	int16 frameNum = 0;
286 	for (int16 i = _numFrames - 1; i >= 0; i--) {
287 		if (_frameTimes[i] < time) {
288 			frameNum = i;
289 			break;
290 		}
291 	}
292 
293 	if (frameNum != _currentFrameNum) {
294 		_currentFrameNum = frameNum;
295 		newFrame(_currentFrameNum);
296 		triggerRedraw();
297 	}
298 }
299 
setFrameNum(const int16 frameNum)300 void FrameSequence::setFrameNum(const int16 frameNum) {
301 	int16 f = CLIP<int>(frameNum, 0, _numFrames);
302 
303 	if (_currentFrameNum != f) {
304 		_currentFrameNum = f;
305 		setTime(_frameTimes[f]);
306 		newFrame(f);
307 		triggerRedraw();
308 	}
309 }
310 
isSequenceOpen() const311 bool FrameSequence::isSequenceOpen() const {
312 	return _numFrames != 0;
313 }
314 
Sprite(const DisplayElementID id)315 Sprite::Sprite(const DisplayElementID id) : DisplayElement(id) {
316 	_numFrames = 0;
317 	_currentFrameNum = 0xffffffff;
318 	_currentFrame = 0;
319 }
320 
~Sprite()321 Sprite::~Sprite() {
322 	discardFrames();
323 }
324 
discardFrames()325 void Sprite::discardFrames() {
326 	if (!_frameArray.empty()) {
327 		for (uint32 i = 0; i < _numFrames; i++) {
328 			SpriteFrame *frame = _frameArray[i].frame;
329 			frame->_referenceCount--;
330 			if (frame->_referenceCount == 0)
331 				delete frame;
332 		}
333 
334 		_frameArray.clear();
335 		_numFrames = 0;
336 		_currentFrame = 0;
337 		_currentFrameNum = 0xffffffff;
338 		setBounds(0, 0, 0, 0);
339 	}
340 }
341 
addPICTResourceFrame(const ResIDType pictID,bool transparent,const CoordType left,const CoordType top)342 void Sprite::addPICTResourceFrame(const ResIDType pictID, bool transparent, const CoordType left, const CoordType top) {
343 	SpriteFrame *frame = new SpriteFrame();
344 	frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, pictID, transparent);
345 	addFrame(frame, left, top);
346 }
347 
addFrame(SpriteFrame * frame,const CoordType left,const CoordType top)348 uint32 Sprite::addFrame(SpriteFrame *frame, const CoordType left, const CoordType top) {
349 	SpriteFrameRec frameRecord;
350 	frameRecord.frame = frame;
351 	frameRecord.frameLeft = left;
352 	frameRecord.frameTop = top;
353 	_frameArray.push_back(frameRecord);
354 	_numFrames++;
355 	frame->_referenceCount++;
356 
357 	Common::Rect frameBounds;
358 	frame->getSurfaceBounds(frameBounds);
359 
360 	// 9/3/96
361 	// BB Should this be + left or - left?
362 	frameBounds.moveTo(_bounds.left + left, _bounds.top + top);
363 
364 	frameBounds.extend(_bounds);
365 
366 	if (_bounds != frameBounds)
367 		setBounds(frameBounds);
368 
369 	return _numFrames - 1;
370 }
371 
removeFrame(const uint32 frameNum)372 void Sprite::removeFrame(const uint32 frameNum) {
373 	_frameArray[frameNum].frame->_referenceCount--;
374 	if (_frameArray[frameNum].frame->_referenceCount == 0)
375 		delete _frameArray[frameNum].frame;
376 
377 	// Calculate the new bounds
378 	Common::Rect frameBounds;
379 	for (uint32 i = 0; i < _numFrames; i++) {
380 		if (i == frameNum)
381 			continue;
382 
383 		Common::Rect r;
384 		_frameArray[i].frame->getSurfaceBounds(r);
385 		r.translate(_frameArray[i].frameLeft, _frameArray[i].frameTop);
386 		frameBounds.extend(r);
387 	}
388 
389 	_frameArray.remove_at(frameNum);
390 
391 	frameBounds.moveTo(_bounds.left, _bounds.top);
392 	setBounds(frameBounds);
393 
394 	if (_currentFrameNum == frameNum)
395 		triggerRedraw();
396 	else if (_currentFrameNum != 0xffffffff && _currentFrameNum > frameNum)
397 		--_currentFrameNum;
398 }
399 
setCurrentFrameIndex(const int32 frameNum)400 void Sprite::setCurrentFrameIndex(const int32 frameNum) {
401 	if (frameNum < 0) {
402 		if (_currentFrameNum != 0xffffffff) {
403 			_currentFrameNum = 0xffffffff;
404 			_currentFrame = 0;
405 			triggerRedraw();
406 		}
407 	} else if (_numFrames > 0) {
408 		uint32 f = frameNum % _numFrames;
409 		if (f != _currentFrameNum) {
410 			_currentFrameNum = f;
411 			_currentFrame = &_frameArray[f];
412 			triggerRedraw();
413 		}
414 	}
415 }
416 
getFrame(const int32 index)417 SpriteFrame *Sprite::getFrame(const int32 index) {
418 	if (index < 0 || (uint32)index >= _numFrames)
419 		return 0;
420 
421 	return _frameArray[index].frame;
422 }
423 
draw(const Common::Rect & r)424 void Sprite::draw(const Common::Rect &r) {
425 	if (_currentFrame) {
426 		Common::Rect frameBounds;
427 		_currentFrame->frame->getSurfaceBounds(frameBounds);
428 
429 		frameBounds.translate(_bounds.left + _currentFrame->frameLeft, _bounds.top + _currentFrame->frameTop);
430 		Common::Rect r1 = frameBounds.findIntersectingRect(r);
431 
432 		Common::Rect r2 = frameBounds;
433 		r2.translate(-_bounds.left - _currentFrame->frameLeft, -_bounds.top - _currentFrame->frameTop);
434 
435 		_currentFrame->frame->drawImage(r2, r1);
436 	}
437 }
438 
SpriteSequence(const DisplayElementID id,const DisplayElementID spriteID)439 SpriteSequence::SpriteSequence(const DisplayElementID id, const DisplayElementID spriteID) :
440 		FrameSequence(id), _sprite(spriteID), _transparent(false) {
441 }
442 
openFrameSequence()443 void SpriteSequence::openFrameSequence() {
444 	if (!isSequenceOpen()) {
445 		FrameSequence::openFrameSequence();
446 
447 		if (isSequenceOpen()) {
448 			uint32 numFrames = getNumFrames();
449 
450 			for (uint32 i = 0; i < numFrames; ++i) {
451 				SpriteFrame *frame = new SpriteFrame();
452 				frame->initFromPICTResource(_resFork, i + 0x80, _transparent);
453 				_sprite.addFrame(frame, 0, 0);
454 			}
455 
456 			_sprite.setBounds(_bounds);
457 		}
458 	}
459 }
460 
closeFrameSequence()461 void SpriteSequence::closeFrameSequence() {
462 	if (isSequenceOpen()) {
463 		FrameSequence::closeFrameSequence();
464 		_sprite.discardFrames();
465 	}
466 }
467 
setBounds(const Common::Rect & bounds)468 void SpriteSequence::setBounds(const Common::Rect &bounds) {
469 	FrameSequence::setBounds(bounds);
470 	_sprite.setBounds(_bounds);
471 }
472 
draw(const Common::Rect & r)473 void SpriteSequence::draw(const Common::Rect &r) {
474 	_sprite.draw(r);
475 }
476 
newFrame(const uint16 frame)477 void SpriteSequence::newFrame(const uint16 frame) {
478 	_sprite.setCurrentFrameIndex(frame);
479 }
480 
481 #define DRAW_PIXEL() \
482 	if (bytesPerPixel == 2) \
483 		*((uint16 *)dst) = black; \
484 	else \
485 		*((uint32 *)dst) = black; \
486 	dst += bytesPerPixel
487 
488 #define SKIP_PIXEL() \
489 	dst += bytesPerPixel
490 
draw(const Common::Rect & r)491 void ScreenDimmer::draw(const Common::Rect &r) {
492 	// We're going to emulate QuickDraw's srcOr+gray mode here
493 	// In this mode, every other y column is all black (odd-columns).
494 	// Basically, every row does three black and then one transparent
495 	// repeatedly.
496 
497 	// The output is identical to the original
498 
499 	uint32 black = g_system->getScreenFormat().RGBToColor(0, 0, 0);
500 	Graphics::Surface *screen = ((PegasusEngine *)g_engine)->_gfx->getWorkArea();
501 	byte bytesPerPixel = g_system->getScreenFormat().bytesPerPixel;
502 
503 	// We're currently doing it to the whole screen to simplify the code
504 
505 	for (int y = 0; y < 480; y++) {
506 		byte *dst = (byte *)screen->getBasePtr(0, y);
507 
508 		for (int x = 0; x < 640; x += 4) {
509 			if (y & 1) {
510 				DRAW_PIXEL();
511 				DRAW_PIXEL();
512 				SKIP_PIXEL();
513 				DRAW_PIXEL();
514 			} else {
515 				SKIP_PIXEL();
516 				DRAW_PIXEL();
517 				DRAW_PIXEL();
518 				DRAW_PIXEL();
519 			}
520 		}
521 	}
522 }
523 
524 #undef DRAW_PIXEL
525 #undef SKIP_PIXEL
526 
SoundLevel(const DisplayElementID id)527 SoundLevel::SoundLevel(const DisplayElementID id) : DisplayElement(id) {
528 	_soundLevel = 0;
529 }
530 
incrementLevel()531 void SoundLevel::incrementLevel() {
532 	if (_soundLevel < 12) {
533 		_soundLevel++;
534 		triggerRedraw();
535 	}
536 }
537 
decrementLevel()538 void SoundLevel::decrementLevel() {
539 	if (_soundLevel > 0) {
540 		_soundLevel--;
541 		triggerRedraw();
542 	}
543 }
544 
getSoundLevel()545 uint16 SoundLevel::getSoundLevel() {
546 	return CLIP<int>(_soundLevel * 22, 0, 256);
547 }
548 
setSoundLevel(uint16 level)549 void SoundLevel::setSoundLevel(uint16 level) {
550 	uint16 newLevel = (level + 21) / 22;
551 
552 	if (newLevel != _soundLevel) {
553 		_soundLevel = newLevel;
554 		triggerRedraw();
555 	}
556 }
557 
draw(const Common::Rect & r)558 void SoundLevel::draw(const Common::Rect &r) {
559 	Common::Rect levelRect(_bounds.right + (8 * (_soundLevel - 12)), _bounds.top, _bounds.right, _bounds.bottom);
560 	levelRect = r.findIntersectingRect(levelRect);
561 
562 	if (!levelRect.isEmpty()) {
563 		Graphics::Surface *screen = ((PegasusEngine *)g_engine)->_gfx->getWorkArea();
564 		screen->fillRect(levelRect, g_system->getScreenFormat().RGBToColor(0, 0, 0));
565 	}
566 }
567 
568 } // End of namespace Pegasus
569