aboutsummaryrefslogtreecommitdiff
path: root/engines/mads/msurface.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/mads/msurface.cpp')
-rw-r--r--engines/mads/msurface.cpp572
1 files changed, 572 insertions, 0 deletions
diff --git a/engines/mads/msurface.cpp b/engines/mads/msurface.cpp
new file mode 100644
index 0000000000..839882a354
--- /dev/null
+++ b/engines/mads/msurface.cpp
@@ -0,0 +1,572 @@
+/* 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/screen.h"
+#include "mads/mads.h"
+#include "mads/msurface.h"
+#include "mads/resources.h"
+#include "mads/sprites.h"
+
+namespace MADS {
+
+MADSEngine *MSurface::_vm = nullptr;
+
+MSurface::MSurface() {
+ pixels = nullptr;
+ _freeFlag = false;
+}
+
+MSurface::MSurface(int width, int height) {
+ pixels = nullptr;
+ _freeFlag = false;
+ setSize(width, height);
+}
+
+MSurface::~MSurface() {
+ if (_freeFlag)
+ Graphics::Surface::free();
+}
+
+void MSurface::setSize(int width, int height) {
+ if (_freeFlag)
+ Graphics::Surface::free();
+ Graphics::Surface::create(width, height, Graphics::PixelFormat::createFormatCLUT8());
+ _freeFlag = true;
+}
+
+void MSurface::setPixels(byte *pData, int horizSize, int vertSize) {
+ _freeFlag = false;
+ pixels = pData;
+ w = pitch = horizSize;
+ h = vertSize;
+ format.bytesPerPixel = 1;
+}
+
+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), 0);
+}
+
+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::copyFrom(MSurface *src, const Common::Point &destPos, int depth,
+ DepthSurface *depthSurface, int scale, int transparentColor) {
+
+ int destX = destPos.x, destY = destPos.y;
+ if (scale == 100) {
+ // Copy the specified area
+ Common::Rect copyRect(0, 0, src->getWidth(), src->getHeight());
+
+ 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;
+
+ byte *data = src->getData();
+ byte *srcPtr = data + (src->getWidth() * copyRect.top + copyRect.left);
+ byte *destPtr = (byte *)pixels + (destY * pitch) + destX;
+
+ // 100% scaling variation
+ for (int rowCtr = 0; rowCtr < copyRect.height(); ++rowCtr) {
+ // Copy each byte one at a time checking against the depth
+ for (int xCtr = 0; xCtr < copyRect.width(); ++xCtr) {
+ int pixelDepth = depthSurface == nullptr ? 15 :
+ depthSurface->getDepth(Common::Point(destX + xCtr, destY + rowCtr));
+ if ((depth <= pixelDepth) && (srcPtr[xCtr] != transparentColor))
+ destPtr[xCtr] = srcPtr[xCtr];
+ }
+
+ srcPtr += src->getWidth();
+ destPtr += getWidth();
+ }
+
+ return;
+ }
+
+ // Start of draw logic for scaled sprites
+ const byte *srcPixelsP = src->getData();
+
+ int destRight = this->getWidth() - 1;
+ int destBottom = this->getHeight() - 1;
+ bool normalFrame = true;
+ int frameWidth = src->getWidth();
+ int frameHeight = src->getHeight();
+
+ int highestDim = MAX(frameWidth, frameHeight);
+ bool lineDist[MADS_SCREEN_WIDTH];
+ int distIndex = 0;
+ int distXCount = 0, distYCount = 0;
+
+ int distCtr = 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 -= distXCount / 2;
+ destY -= distYCount - 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;
+
+ int spriteRight = spriteLeft + spriteWidth;
+ if (spriteWidth <= 0)
+ return;
+ if (!normalFrame) {
+ destX += distXCount - 1;
+ spriteLeft = -(distXCount - spriteRight);
+ spriteRight = (-spriteLeft + spriteWidth);
+ }
+
+ // 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 = this->getBasePtr(destX + spriteLeft, destY + spriteTop);
+
+ spriteLeft = (spriteLeft * (normalFrame ? 1 : -1));
+
+ // Loop through the lines of the sprite
+ for (int yp = 0, sprY = -1; yp < frameHeight; ++yp, srcPixelsP += src->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;
+
+ 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;
+
+ // Get depth of current output pixel in depth surface
+ Common::Point pt((destP - (byte *)this->pixels) % this->pitch,
+ (destP - (byte *)this->pixels) / this->pitch);
+ int pixelDepth = (depthSurface == nullptr) ? 15 : depthSurface->getDepth(pt);
+
+ if ((*srcP != transparentColor) && (depth <= pixelDepth))
+ *destP = *srcP;
+
+ ++destP;
+ }
+
+ // Move to the next destination line
+ destPixelsP += this->pitch;
+ }
+}
+
+void MSurface::copyFromScaled(MSurface *src, const Common::Point &destPos, int depth,
+ DepthSurface *depthSurface, int scale, int transparentColor) {
+ int distXCount = 0, distYCount = 0;
+ int highestDim = MAX(src->w, src->h);
+ int accum = 0;
+
+ for (int idx = 0; idx < highestDim; ++idx) {
+ accum += scale;
+ if (accum >= 100) {
+ accum -= 100;
+ if (idx < src->w)
+ ++distXCount;
+ if (idx < src->h)
+ ++distYCount;
+ }
+ }
+
+ Common::Point newPos(destPos.x - distXCount / 2, destPos.y - distYCount);
+ copyFrom(src, src->getBounds(), newPos, transparentColor);
+}
+
+void MSurface::scrollX(int xAmount) {
+ if (xAmount == 0)
+ return;
+
+ byte buffer[80];
+ int direction = (xAmount > 0) ? -1 : 1;
+ int xSize = ABS(xAmount);
+ assert(xSize <= 80);
+
+ byte *srcP = getBasePtr(0, 0);
+
+ for (int y = 0; y < this->h; ++y, srcP += pitch) {
+ if (direction < 0) {
+ // Copy area to be overwritten
+ Common::copy(srcP, srcP + xSize, &buffer[0]);
+ // Shift the remainder of the line over the given area
+ Common::copy(srcP + xSize, srcP + this->w, srcP);
+ // Move buffered area to the end of the line
+ Common::copy(&buffer[0], &buffer[xSize], srcP + this->w - xSize);
+ } else {
+ // Copy area to be overwritten
+ Common::copy_backward(srcP + this->w - xSize, srcP + this->w, &buffer[80]);
+ // Shift the remainder of the line over the given area
+ Common::copy_backward(srcP, srcP + this->w - xSize, srcP + this->w);
+ // Move buffered area to the start of the line
+ Common::copy_backward(&buffer[80 - xSize], &buffer[80], srcP + xSize);
+ }
+ }
+}
+
+void MSurface::scrollY(int yAmount) {
+ if (yAmount == 0)
+ return;
+
+ int direction = (yAmount > 0) ? 1 : -1;
+ int ySize = ABS(yAmount);
+ assert(ySize < (this->h / 2));
+ assert(this->w == pitch);
+
+ int blockSize = ySize * this->w;
+ byte *tempData = new byte[blockSize];
+ byte *pixelsP = getBasePtr(0, 0);
+
+ if (direction > 0) {
+ // Buffer the lines to be overwritten
+ byte *srcP = (byte *)getBasePtr(0, this->h - ySize);
+ Common::copy(srcP, srcP + (pitch * ySize), tempData);
+ // Vertically shift all the lines
+ Common::copy_backward(pixelsP, pixelsP + (pitch * (this->h - ySize)),
+ pixelsP + (pitch * this->h));
+ // Transfer the buffered lines top the top of the screen
+ Common::copy(tempData, tempData + blockSize, pixelsP);
+ } else {
+ // Buffer the lines to be overwritten
+ Common::copy(pixelsP, pixelsP + (pitch * ySize), tempData);
+ // Vertically shift all the lines
+ Common::copy(pixelsP + (pitch * ySize), pixelsP + (pitch * this->h), pixelsP);
+ // Transfer the buffered lines to the bottom of the screen
+ Common::copy(tempData, tempData + blockSize, pixelsP + (pitch * (this->h - ySize)));
+ }
+
+ delete[] tempData;
+}
+
+
+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) {
+ if (*pDest < 255) // scene 752 has some palette indices of 255
+ *pDest = palette[*pDest]._palIndex;
+ }
+ }
+}
+
+void MSurface::translate(byte map[PALETTE_COUNT]) {
+ for (int y = 0; y < this->h; ++y) {
+ byte *pDest = getBasePtr(0, y);
+
+ for (int x = 0; x < this->w; ++x, ++pDest) {
+ *pDest = map[*pDest];
+ }
+ }
+}
+
+MSurface *MSurface::flipHorizontal() const {
+ MSurface *dest = new MSurface(this->w, this->h);
+
+ for (int y = 0; y < this->h; ++y) {
+ const byte *srcP = getBasePtr(this->w - 1, y);
+ byte *destP = dest->getBasePtr(0, y);
+
+ for (int x = 0; x < this->w; ++x)
+ *destP++ = *srcP--;
+ }
+
+ return dest;
+}
+
+/*------------------------------------------------------------------------*/
+
+int DepthSurface::getDepth(const Common::Point &pt) {
+ if (_vm->_game->_scene._sceneInfo->_depthStyle == 2) {
+ int bits = (3 - (pt.x % 4)) * 2;
+ byte v = *getBasePtr(pt.x >> 2, pt.y);
+ return v >> bits;
+ } else {
+ if (pt.x < 0 || pt.y < 0 || pt.x >= this->w || pt.y >= this->h)
+ return 0;
+
+ return *getBasePtr(pt.x, pt.y) & 0xF;
+ }
+}
+
+int DepthSurface::getDepthHighBit(const Common::Point &pt) {
+ if (_vm->_game->_scene._sceneInfo->_depthStyle == 2) {
+ int bits = (3 - (pt.x % 4)) * 2;
+ byte v = *getBasePtr(pt.x >> 2, pt.y);
+ return (v >> bits) & 2;
+ } else {
+ if (pt.x < 0 || pt.y < 0 || pt.x >= this->w || pt.y >= this->h)
+ return 0;
+
+ return *getBasePtr(pt.x, pt.y) & 0x80;
+ }
+}
+
+
+} // End of namespace MADS