PEGASUS: Add the panorama class

This commit is contained in:
Matthew Hoops 2011-11-01 12:17:22 -04:00
parent 48cabb18d6
commit dec8fcdbbd
3 changed files with 339 additions and 0 deletions

View File

@ -78,6 +78,7 @@ MODULE_OBJS = \
neighborhood/norad/subplatform.o \
neighborhood/norad/alpha/fillingstation.o \
neighborhood/norad/alpha/noradalpha.o \
neighborhood/norad/alpha/panorama.o \
neighborhood/prehistoric/prehistoric.o \
neighborhood/tsa/fulltsa.o \
neighborhood/tsa/tinytsa.o \

View File

@ -0,0 +1,240 @@
/* 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.
*
* Additional copyright for this file:
* Copyright (C) 1995-1997 Presto Studios, Inc.
*
* 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 "common/macresman.h"
#include "common/stream.h"
#include "pegasus/neighborhood/norad/alpha/panorama.h"
namespace Pegasus {
Panorama::Panorama() : _panoramaMovie(kNoDisplayElement) {
blankFields();
}
Panorama::~Panorama() {
releasePanorama();
}
void Panorama::blankFields() {
_viewBounds = Common::Rect();
_drawBounds = Common::Rect();
_mask = 0;
_panoramaWidth = 0;
_panoramaHeight = 0;
_stripWidth = 0;
_stripLeft = -1;
_stripRight = -1;
}
void Panorama::releasePanorama() {
if (_panoramaMovie.isMovieValid()) {
_panoramaMovie.releaseMovie();
_panoramaWorld.deallocateSurface();
blankFields();
}
}
static const uint32 kPanoramaResType = MKTAG('P', 'a', 'n', 'I'); // Panorama Information.
static const uint16 kPanoramaResID = 128;
void Panorama::initFromMovieFile(const Common::String &fileName) {
// First, we need the resource fork for other reasons -- PanI resource
Common::MacResManager *resFork = new Common::MacResManager();
if (!resFork->open(fileName) || !resFork->hasResFork())
error("Could not open the resource fork of '%s'", fileName.c_str());
Common::SeekableReadStream *resource = resFork->getResource(kPanoramaResType, kPanoramaResID);
if (!resource)
error("No panorama information in the resource fork of '%s'", fileName.c_str());
_panoramaWidth = resource->readUint16BE();
_panoramaHeight = resource->readUint16BE();
_stripWidth = resource->readUint16BE();
delete resource;
delete resFork;
// Now we open the movie like normal
_panoramaMovie.initFromMovieFile(fileName);
}
void Panorama::setMask(Surface *mask) {
_mask = mask;
}
// If the panorama is not open, do nothing and return.
// Otherwise, set up the view bounds.
void Panorama::setViewBounds(const Common::Rect &newView) {
if (!isPanoramaOpen())
return;
if (newView.isEmpty())
return;
Common::Rect r = newView;
if (r.width() > _panoramaWidth) {
r.left = 0;
r.right = _panoramaWidth;
} else {
if (r.right > _panoramaWidth)
r.translate(_panoramaWidth - r.right, 0);
if (r.left < 0)
r.translate(-r.left, 0);
}
if (r.height() > _panoramaHeight) {
r.top = 0;
r.bottom = _panoramaHeight;
} else {
if (r.bottom > _panoramaHeight)
r.translate(0, _panoramaHeight - r.bottom);
if (r.top < 0)
r.translate(0, -r.top);
}
if (_viewBounds != r) {
tCoordType stripLeft = 0;
if (r.width() != _viewBounds.width() || !_panoramaWorld.isSurfaceValid()) {
_panoramaWorld.deallocateSurface();
makeNewSurface(r);
} else {
tCoordType stripRight;
calcStripRange(r, stripLeft, stripRight);
loadStrips(stripLeft, stripRight);
}
_viewBounds = r;
_drawBounds = r;
_drawBounds.translate(-stripLeft * _stripWidth, 0);
}
}
void Panorama::getViewBounds(Common::Rect &r) const {
r = _viewBounds;
}
void Panorama::getPanoramaBounds(Common::Rect &r) const {
r = Common::Rect(0, 0, _panoramaWidth, _panoramaHeight);
}
void Panorama::drawPanorama(const Common::Rect &destRect) {
if (_panoramaWorld.isSurfaceValid()) {
if (_mask)
_panoramaWorld.copyToCurrentPortMasked(_drawBounds, destRect, _mask);
else
_panoramaWorld.copyToCurrentPortTransparent(_drawBounds, destRect);
}
}
// Make a new Surface big enough to show r, which is assumed to be a valid view bounds.
// Assumptions:
// r is a valid view bounds.
// _panoramaWorld is not allocated.
// _panoramaHeight, _stripWidth is correct.
// _panoramaMovie is allocated.
void Panorama::makeNewSurface(const Common::Rect& view) {
tCoordType stripLeft, stripRight;
calcStripRange(view, stripLeft, stripRight);
Common::Rect r(0, 0, (stripRight - stripLeft + 1) * _stripWidth, _panoramaHeight);
_panoramaWorld.allocateSurface(r);
_panoramaMovie.shareSurface(&_panoramaWorld);
loadStrips(stripLeft, stripRight);
}
// Assumes view is not empty.
void Panorama::calcStripRange(const Common::Rect &view, tCoordType &stripLeft, tCoordType &stripRight) {
stripLeft = view.left / _stripWidth;
stripRight = (view.left - view.left % _stripWidth + _stripWidth - 1 + view.width()) / _stripWidth;
}
// Load in all needed strips to put range (stripLeft, stripRight) into the
// panorama's Surface. Try to optimize by saving any pixels already in the Surface.
// Assumptions:
// Surface is allocated and is big enough for maximum range of
// stripLeft and stripRight
void Panorama::loadStrips(tCoordType stripLeft, tCoordType stripRight) {
if (_stripLeft == -1) {
// Surface has just been allocated.
// Load in all strips.
for (tCoordType i = stripLeft; i <= stripRight; i++)
loadOneStrip(i, stripLeft);
_stripLeft = stripLeft;
_stripRight = stripRight;
} else if (stripLeft != _stripLeft) {
tCoordType overlapLeft = MAX(stripLeft, _stripLeft);
tCoordType overlapRight = MIN(stripRight, _stripRight);
if (overlapLeft <= overlapRight) {
Common::Rect r1((overlapLeft - _stripLeft) * _stripWidth, 0,
(overlapRight - _stripLeft + 1) * _stripWidth, _panoramaHeight);
Common::Rect r2 = r1;
if (stripLeft < _stripLeft) {
Common::Rect bounds;
_panoramaWorld.getSurfaceBounds(bounds);
_panoramaWorld.getSurface()->move(bounds.right - r1.right, 0, _panoramaHeight);
for (tCoordType i = stripLeft; i < _stripLeft; i++)
loadOneStrip(i, stripLeft);
} else {
_panoramaWorld.getSurface()->move(-r1.left, 0, _panoramaHeight);
for (tCoordType i = _stripRight + 1; i <= stripRight; i++)
loadOneStrip(i, stripLeft);
}
} else {
// No overlap.
// Load everything.
for (tCoordType i = stripLeft; i <= stripRight; i++)
loadOneStrip(i, stripLeft);
}
_stripLeft = stripLeft;
_stripRight = stripRight;
} else if (stripRight > _stripRight) {
// Need to add one or more strips.
for (tCoordType i = _stripRight + 1; i <= stripRight; i++)
loadOneStrip(i, _stripLeft);
_stripRight = stripRight;
} else if (stripRight < _stripRight) {
// Need to chop off one strip.
_stripRight = stripRight;
}
}
void Panorama::loadOneStrip(tCoordType stripToLoad, tCoordType leftStrip) {
_panoramaMovie.moveMovieBoxTo((stripToLoad - leftStrip) * _stripWidth, 0);
_panoramaMovie.setTime(stripToLoad);
_panoramaMovie.redrawMovieWorld();
}
} // End of namespace Pegasus

View File

@ -0,0 +1,98 @@
/* 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.
*
* Additional copyright for this file:
* Copyright (C) 1995-1997 Presto Studios, Inc.
*
* 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 PEGASUS_NEIGHBORHOOD_NORAD_ALPHA_PANORAMA_H
#define PEGASUS_NEIGHBORHOOD_NORAD_ALPHA_PANORAMA_H
#include "pegasus/movie.h"
namespace Pegasus {
/*
Panorama implements a wide image using a specially constructed movie file.
The movie holds the image as a series of vertical strips, say 16 or 32 pixels wide.
The panorama bounds defines the entire panorama. The view bounds represents the
area on the panorama that is kept in memory.
The panorama bounds is also stored in the movie file; it cannot be changed. The
view bounds must always be a subset of the panorama bounds.
In actuality, the area kept in memory is at least as wide as the view bounds (but
may be wider to coincide with the width of the movies slices), and is as tall as
the panorama bounds. The view bounds is used by the drawPanorama function to draw
a piece of the panorama to the current screen.
The panorama movie is built at a time scale of 1, with each strip lasting for one
second, so that strip number corresponds exactly with the time value at which the
strip is stored.
TO USE:
Call one initFromMovieFile to open the movie. Then set up a view rect by
calling setViewBounds. Once these two functions have been called, drawPanorama
will draw the panorama.
*/
class Panorama {
public:
Panorama();
virtual ~Panorama();
void initFromMovieFile(const Common::String &);
void releasePanorama();
bool isPanoramaOpen() { return _panoramaMovie.isMovieValid(); }
void setViewBounds(const Common::Rect &);
void getViewBounds(Common::Rect &) const;
void setMask(Surface *);
void getPanoramaBounds(Common::Rect &) const;
void drawPanorama(const Common::Rect &);
protected:
void blankFields();
void makeNewSurface(const Common::Rect &);
void calcStripRange(const Common::Rect &, tCoordType &, tCoordType &);
void loadStrips(tCoordType, tCoordType);
void loadOneStrip(tCoordType, tCoordType);
Movie _panoramaMovie;
Surface _panoramaWorld, *_mask;
Common::Rect _viewBounds;
Common::Rect _drawBounds;
tCoordType _panoramaWidth, _panoramaHeight;
tCoordType _stripWidth; // Pixels per strip.
tCoordType _numStrips;
tCoordType _stripLeft, _stripRight;
};
} // End of namespace Pegasus
#endif