mirror of
https://github.com/libretro/scummvm.git
synced 2025-01-13 13:10:53 +00:00
263 lines
6.7 KiB
C++
263 lines
6.7 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 "engines/util.h"
|
|
#include "mads/compression.h"
|
|
#include "mads/graphics.h"
|
|
#include "mads/mads.h"
|
|
#include "mads/msprite.h"
|
|
#include "mads/msurface.h"
|
|
#include "mads/resources.h"
|
|
|
|
namespace MADS {
|
|
|
|
MADSEngine *MSurface::_vm = nullptr;
|
|
|
|
MSurface::MSurface() {
|
|
pixels = nullptr;
|
|
}
|
|
|
|
MSurface::MSurface(int width, int height) {
|
|
pixels = nullptr;
|
|
setSize(width, height);
|
|
}
|
|
|
|
MSurface::~MSurface() {
|
|
Graphics::Surface::free();
|
|
}
|
|
|
|
void MSurface::setSize(int width, int height) {
|
|
Graphics::Surface::free();
|
|
Graphics::Surface::create(width, height, Graphics::PixelFormat::createFormatCLUT8());
|
|
}
|
|
|
|
int MSurface::scaleValue(int value, int scale, int err) {
|
|
int scaled = 0;
|
|
while (value--) {
|
|
err -= scale;
|
|
while (err < 0) {
|
|
scaled++;
|
|
err += 100;
|
|
}
|
|
}
|
|
return scaled;
|
|
}
|
|
|
|
void MSurface::drawSprite(const Common::Point &pt, SpriteInfo &info, const Common::Rect &clipRect) {
|
|
|
|
enum {
|
|
kStatusSkip,
|
|
kStatusScale,
|
|
kStatusDraw
|
|
};
|
|
|
|
// NOTE: The current clipping code assumes that the top left corner of the clip
|
|
// rectangle is always 0, 0
|
|
assert(clipRect.top == 0 && clipRect.left == 0);
|
|
|
|
// TODO: Put err* and scaled* into SpriteInfo
|
|
int errX = info.hotX * info.scaleX % 100;
|
|
int errY = info.hotY * info.scaleY % 100;
|
|
int scaledWidth = scaleValue(info.width, info.scaleX, errX);
|
|
int scaledHeight = scaleValue(info.height, info.scaleY, errY);
|
|
|
|
int x = pt.x, y = pt.y;
|
|
int clipX = 0, clipY = 0;
|
|
// Clip the sprite's width and height according to the clip rectangle's dimensions
|
|
// This clips the sprite to the bottom and right
|
|
if (x >= 0) {
|
|
scaledWidth = MIN<int>(x + scaledWidth, clipRect.right) - x;
|
|
} else {
|
|
clipX = x;
|
|
scaledWidth = x + scaledWidth;
|
|
}
|
|
if (y >= 0) {
|
|
scaledHeight = MIN<int>(y + scaledHeight, clipRect.bottom) - y;
|
|
} else {
|
|
clipY = y;
|
|
scaledHeight = y + scaledHeight;
|
|
}
|
|
|
|
// Check if sprite is inside the screen. If it's not, there's no need to draw it
|
|
if (scaledWidth + x <= 0 || scaledHeight + y <= 0) // check left and top (in case x,y are negative)
|
|
return;
|
|
if (scaledWidth <= 0 || scaledHeight <= 0) // check right and bottom
|
|
return;
|
|
int heightAmt = scaledHeight;
|
|
|
|
byte *src = info.sprite->getData();
|
|
byte *dst = getBasePtr(x - info.hotX - clipX, y - info.hotY - clipY);
|
|
|
|
int status = kStatusSkip;
|
|
byte *scaledLineBuf = new byte[scaledWidth];
|
|
|
|
while (heightAmt > 0) {
|
|
|
|
if (status == kStatusSkip) {
|
|
// Skip line
|
|
errY -= info.scaleY;
|
|
if (errY < 0)
|
|
status = kStatusScale;
|
|
else
|
|
src += info.width;
|
|
} else {
|
|
|
|
if (status == kStatusScale) {
|
|
// Scale current line
|
|
byte *lineDst = scaledLineBuf;
|
|
int curErrX = errX;
|
|
int width = scaledWidth;
|
|
byte *tempSrc = src;
|
|
int startX = clipX;
|
|
while (width > 0) {
|
|
byte pixel = *tempSrc++;
|
|
curErrX -= info.scaleX;
|
|
while (curErrX < 0) {
|
|
if (startX == 0) {
|
|
*lineDst++ = pixel;
|
|
width--;
|
|
} else {
|
|
startX++;
|
|
}
|
|
curErrX += 100;
|
|
}
|
|
}
|
|
src += info.width;
|
|
status = kStatusDraw;
|
|
}
|
|
|
|
if (status == kStatusDraw && clipY == 0) {
|
|
// Draw previously scaled line
|
|
// TODO Implement different drawing types (depth, shadow etc.)
|
|
byte *tempDst = dst;
|
|
for (int lineX = 0; lineX < scaledWidth; lineX++) {
|
|
byte pixel = scaledLineBuf[lineX];
|
|
|
|
if (info.encoding & 0x80) {
|
|
|
|
if (pixel == 0x80) {
|
|
pixel = 0;
|
|
} else {
|
|
byte destPixel = *tempDst;
|
|
byte r, g, b;
|
|
r = CLIP((info.palette[destPixel * 3] * pixel) >> 10, 0, 31);
|
|
g = CLIP((info.palette[destPixel * 3 + 1] * pixel) >> 10, 0, 31);
|
|
b = CLIP((info.palette[destPixel * 3 + 2] * pixel) >> 10, 0, 31);
|
|
pixel = info.inverseColorTable[(b << 10) | (g << 5) | r];
|
|
}
|
|
}
|
|
|
|
if (pixel)
|
|
*tempDst = pixel;
|
|
|
|
tempDst++;
|
|
}
|
|
dst += pitch;
|
|
heightAmt--;
|
|
// TODO depth etc.
|
|
//depthAddress += Destination -> Width;
|
|
|
|
errY += 100;
|
|
if (errY >= 0)
|
|
status = kStatusSkip;
|
|
} else if (status == kStatusDraw && clipY < 0) {
|
|
clipY++;
|
|
|
|
errY += 100;
|
|
if (errY >= 0)
|
|
status = kStatusSkip;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
delete[] scaledLineBuf;
|
|
|
|
}
|
|
|
|
void MSurface::empty() {
|
|
Common::fill(getBasePtr(0, 0), getBasePtr(0, h), _vm->_palette->BLACK);
|
|
}
|
|
|
|
void MSurface::updateScreen() {
|
|
g_system->copyRectToScreen((const byte *)pixels, pitch, 0, 0, w, h);
|
|
g_system->updateScreen();
|
|
}
|
|
|
|
void MSurface::copyFrom(MSurface *src, const Common::Rect &srcBounds,
|
|
const Common::Point &destPos, int transparentColor) {
|
|
// Validation of the rectangle and position
|
|
int destX = destPos.x, destY = destPos.y;
|
|
if ((destX >= w) || (destY >= h))
|
|
return;
|
|
|
|
Common::Rect copyRect = srcBounds;
|
|
if (destX < 0) {
|
|
copyRect.left += -destX;
|
|
destX = 0;
|
|
} else if (destX + copyRect.width() > w) {
|
|
copyRect.right -= destX + copyRect.width() - w;
|
|
}
|
|
if (destY < 0) {
|
|
copyRect.top += -destY;
|
|
destY = 0;
|
|
} else if (destY + copyRect.height() > h) {
|
|
copyRect.bottom -= destY + copyRect.height() - h;
|
|
}
|
|
|
|
if (!copyRect.isValidRect())
|
|
return;
|
|
|
|
// Copy the specified area
|
|
|
|
byte *data = src->getData();
|
|
byte *srcPtr = data + (src->getWidth() * copyRect.top + copyRect.left);
|
|
byte *destPtr = (byte *)pixels + (destY * getWidth()) + destX;
|
|
|
|
for (int rowCtr = 0; rowCtr < copyRect.height(); ++rowCtr) {
|
|
if (transparentColor == -1)
|
|
// No transparency, so copy line over
|
|
Common::copy(srcPtr, srcPtr + copyRect.width(), destPtr);
|
|
else {
|
|
// Copy each byte one at a time checking for the transparency color
|
|
for (int xCtr = 0; xCtr < copyRect.width(); ++xCtr)
|
|
if (srcPtr[xCtr] != transparentColor) destPtr[xCtr] = srcPtr[xCtr];
|
|
}
|
|
|
|
srcPtr += src->getWidth();
|
|
destPtr += getWidth();
|
|
}
|
|
}
|
|
|
|
void MSurface::translate(Common::Array<RGB6> &palette) {
|
|
for (int y = 0; y < this->h; ++y) {
|
|
byte *pDest = getBasePtr(0, y);
|
|
|
|
for (int x = 0; x < this->w; ++x, ++pDest) {
|
|
*pDest = palette[*pDest].palIndex;
|
|
}
|
|
}
|
|
}
|
|
|
|
} // End of namespace MADS
|