scummvm/engines/xeen/xsurface.cpp
2015-01-19 22:38:33 -05:00

219 lines
5.6 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.
*
*/
#include "common/algorithm.h"
#include "common/util.h"
#include "xeen/xsurface.h"
#include "xeen/resources.h"
#include "xeen/screen.h"
namespace Xeen {
XSurface::XSurface() : Graphics::Surface(), _freeFlag(false) {
}
XSurface::XSurface(int w, int h) : Graphics::Surface(), _freeFlag(false) {
create(w, h);
}
XSurface::~XSurface() {
if (_freeFlag)
free();
}
void XSurface::create(uint16 w, uint16 h) {
Graphics::Surface::create(w, h, Graphics::PixelFormat::createFormatCLUT8());
_freeFlag = true;
}
void XSurface::create(XSurface *s, const Common::Rect &bounds) {
pixels = (byte *)s->getBasePtr(bounds.left, bounds.top);
format = Graphics::PixelFormat::createFormatCLUT8();
pitch = s->pitch;
w = bounds.width();
h = bounds.height();
_freeFlag = false;
}
void XSurface::transBlitTo(XSurface &dest) const {
transBlitTo(dest, Common::Point());
}
void XSurface::blitTo(XSurface &dest) const {
blitTo(dest, Common::Point());
}
void XSurface::transBlitTo(XSurface &dest, const Common::Point &destPos) const {
if (dest.getPixels() == nullptr)
dest.create(w, h);
for (int yp = 0; yp < h; ++yp) {
const byte *srcP = (const byte *)getBasePtr(0, yp);
byte *destP = (byte *)dest.getBasePtr(destPos.x, destPos.y + yp);
for (int xp = 0; xp < w; ++xp, ++srcP, ++destP) {
if (*srcP != 0)
*destP = *srcP;
}
}
dest.addDirtyRect(Common::Rect(destPos.x, destPos.y, destPos.x + w, destPos.y));
}
void XSurface::transBlitTo(XSurface &dest, const Common::Point &destPos,
int scale, int transparentColor) {
int destX = destPos.x, destY = destPos.y;
int frameWidth = this->w;
int frameHeight = this->h;
int direction = 1;
int highestDim = MAX(frameWidth, frameHeight);
bool lineDist[SCREEN_WIDTH];
int distXCount = 0, distYCount = 0;
if (scale != 0) {
int distCtr = 0;
int distIndex = 0;
do {
distCtr += scale;
if (distCtr < 100) {
lineDist[distIndex] = false;
}
else {
lineDist[distIndex] = true;
distCtr -= 100;
if (distIndex < frameWidth)
++distXCount;
if (distIndex < frameHeight)
++distYCount;
}
} while (++distIndex < highestDim);
destX += (this->w - distXCount) / 2;
destY += (this->h - distYCount) / 2;
}
// Start of draw logic for scaled sprites
const byte *srcPixelsP = (const byte *)getPixels();
int destRight = dest.w - 1;
int destBottom = dest.h - 1;
// Check x bounding area
int spriteLeft = 0;
int spriteWidth = distXCount;
int widthAmount = destX + distXCount - 1;
if (destX < 0) {
spriteWidth += destX;
spriteLeft -= destX;
}
widthAmount -= destRight;
if (widthAmount > 0)
spriteWidth -= widthAmount;
if (spriteWidth <= 0)
return;
// Check y bounding area
int spriteTop = 0;
int spriteHeight = distYCount;
int heightAmount = destY + distYCount - 1;
if (destY < 0) {
spriteHeight += destY;
spriteTop -= destY;
}
heightAmount -= destBottom;
if (heightAmount > 0)
spriteHeight -= heightAmount;
int spriteBottom = spriteTop + spriteHeight;
if (spriteHeight <= 0)
return;
byte *destPixelsP = (byte *)dest.getBasePtr(destX + spriteLeft, destY + spriteTop);
int destWidth = 0, destHeight = 0;
spriteLeft = spriteLeft * direction;
// Loop through the lines of the sprite
for (int yp = 0, sprY = -1; yp < frameHeight; ++yp, srcPixelsP += this->pitch) {
if (!lineDist[yp])
// Not a display line, so skip it
continue;
// Check whether the sprite line is in the display range
++sprY;
if ((sprY >= spriteBottom) || (sprY < spriteTop))
continue;
// Found a line to display. Loop through the pixels
const byte *srcP = srcPixelsP;
byte *destP = destPixelsP;
++destHeight;
for (int xp = 0, sprX = 0; xp < frameWidth; ++xp, ++srcP) {
if (xp < spriteLeft)
// Not yet reached start of display area
continue;
if (!lineDist[sprX++])
// Not a display pixel
continue;
if (*srcP != transparentColor)
*destP = *srcP;
destP += direction;
}
// Keep track of widest line drawn
destWidth = MAX(destP - destPixelsP, destWidth);
// Move to the next destination line
destPixelsP += dest.pitch;
}
// Add a dirty rect for the affected area
dest.addDirtyRect(Common::Rect(destX + spriteLeft, destY + spriteTop,
destX + spriteLeft + destWidth, destY + spriteTop + destHeight));
}
void XSurface::blitTo(XSurface &dest, const Common::Point &destPos) const {
if (dest.getPixels() == nullptr)
dest.create(w, h);
for (int yp = 0; yp < h; ++yp) {
const byte *srcP = (const byte *)getBasePtr(0, yp);
byte *destP = (byte *)dest.getBasePtr(destPos.x, destPos.y + yp);
Common::copy(srcP, srcP + w, destP);
}
dest.addDirtyRect(Common::Rect(destPos.x, destPos.y, destPos.x + w, destPos.y + h));
}
} // End of namespace Xeen