diff options
Diffstat (limited to 'engines/m4/graphics.cpp')
-rw-r--r-- | engines/m4/graphics.cpp | 231 |
1 files changed, 180 insertions, 51 deletions
diff --git a/engines/m4/graphics.cpp b/engines/m4/graphics.cpp index 8624f18da1..423dda5e7e 100644 --- a/engines/m4/graphics.cpp +++ b/engines/m4/graphics.cpp @@ -65,6 +65,15 @@ void RGBList::setRange(int start, int count, const RGB8 *src) { Common::copy(&src[0], &src[count], &_data[start]); } +/** + * Creates a duplicate of the given rgb list + */ +RGBList *RGBList::clone() const { + RGBList *dest = new RGBList(_size, _data, false); + _madsVm->_palette->addRange(dest); + return dest; +} + //-------------------------------------------------------------------------- #define VGA_COLOR_TRANS(x) (x == 0x3f ? 255 : x << 2) @@ -74,6 +83,8 @@ M4Surface::~M4Surface() { _madsVm->_palette->deleteRange(_rgbList); delete _rgbList; } + if (_ownsData) + free(); } void M4Surface::loadCodesM4(Common::SeekableReadStream *source) { @@ -331,6 +342,16 @@ void M4Surface::clear() { Common::set_to((byte *)pixels, (byte *)pixels + w * h, _vm->_palette->BLACK); } +void M4Surface::reset() { + ::free(pixels); + pixels = NULL; + if (_rgbList) { + _vm->_palette->deleteRange(_rgbList); + delete _rgbList; + _rgbList = NULL; + } +} + void M4Surface::frameRect(const Common::Rect &r, uint8 color) { Graphics::Surface::frameRect(r, color); } @@ -389,43 +410,40 @@ void M4Surface::copyFrom(M4Surface *src, const Common::Rect &srcBounds, int dest * Copies a given image onto a destination surface with scaling, transferring only pixels that meet * the specified depth requirement on a secondary surface contain depth information */ -void M4Surface::copyFrom(M4Surface *src, int destX, int destY, int depth, M4Surface *depthsSurface, - int scale, int transparentColour) { - /* TODO: This isn't a straight re-implementation of the original draw routine. Double check in future - * whether this implementation provides equivalent functionality - */ - Common::Rect copyRect(0, 0, src->width(), src->height()); +void M4Surface::copyFrom(M4Surface *src, int destX, int destY, int depth, + M4Surface *depthsSurface, int scale, int transparentColour) { - 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; + if (scale == 100) { + // Copy the specified area + Common::Rect copyRect(0, 0, src->width(), src->height()); + + 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; + } - // Copy the specified area + if (!copyRect.isValidRect()) + return; - byte *data = src->getBasePtr(); - byte *srcPtr = data + (src->width() * copyRect.top + copyRect.left); - byte *depthsData = depthsSurface->getBasePtr(); - byte *depthsPtr = depthsData + (src->width() * copyRect.top + copyRect.left); - byte *destPtr = (byte *)pixels + (destY * width()) + destX; + byte *data = src->getBasePtr(); + byte *srcPtr = data + (src->width() * copyRect.top + copyRect.left); + byte *depthsData = depthsSurface->getBasePtr(); + byte *depthsPtr = depthsData + (depthsSurface->pitch * destY) + destX; + byte *destPtr = (byte *)pixels + (destY * pitch) + destX; - if (scale == 100) { // 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) { - if ((depthsPtr[xCtr] > depth) && (srcPtr[xCtr] != transparentColour)) + if ((depth <= (depthsPtr[xCtr] & 0x7f)) && (srcPtr[xCtr] != transparentColour)) destPtr[xCtr] = srcPtr[xCtr]; } @@ -433,31 +451,127 @@ void M4Surface::copyFrom(M4Surface *src, int destX, int destY, int depth, M4Surf depthsPtr += depthsSurface->width(); destPtr += width(); } - } else { - // Scaled variation - for (int rowCtr = 0; rowCtr < copyRect.height(); ++rowCtr) { - int currX = -1; - // Loop through the source pixels - for (int xCtr = 0, xTotal = 0; xCtr < copyRect.width(); ++xCtr, xTotal += (100 - scale)) { - int srcX = xTotal / 100; + src->freeData(); + depthsSurface->freeData(); + return; + } - if (srcX != currX) { - currX = srcX; + // Start of draw logic for scaled sprites + const byte *srcPixelsP = src->getBasePtr(); + + int destRight = this->width() - 1; + int destBottom = this->height() - 1; + bool normalFrame = true; // TODO: false for negative frame numbers + int frameWidth = src->width(); + int frameHeight = src->height(); + + int highestDim = MAX(frameWidth, frameHeight); + bool lineDist[MADS_SURFACE_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 ((depthsPtr[currX] > depth) && (srcPtr[xCtr] != transparentColour)) - destPtr[currX] = srcPtr[xCtr]; - } - } + if (distIndex < frameWidth) + ++distXCount; - srcPtr += src->width(); - depthsPtr += depthsSurface->width(); - destPtr += width(); + 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); + const byte *depthPixelsP = depthsSurface->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; + const byte *depthP = depthPixelsP; + 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; + + if ((*srcP != transparentColour) && (depth <= (*depthP & 0x7f))) + *destP = *srcP; + + ++destP; + ++depthP; + } + + // Move to the next destination line + destPixelsP += this->pitch; + depthPixelsP += depthsSurface->pitch; } src->freeData(); depthsSurface->freeData(); + this->freeData(); } void M4Surface::loadBackgroundRiddle(const char *sceneName) { @@ -471,8 +585,6 @@ void M4Surface::loadBackgroundRiddle(const char *sceneName) { } void M4Surface::loadBackground(int sceneNumber, RGBList **palData) { - clear(); // clear previous scene - if (_vm->isM4() || (_vm->getGameType() == GType_RexNebular)) { char resourceName[20]; Common::SeekableReadStream *stream; @@ -752,7 +864,7 @@ void M4Surface::scrollX(int xAmount) { return; byte buffer[80]; - int direction = (xAmount > 0) ? 1 : -1; + int direction = (xAmount > 0) ? -1 : 1; int xSize = ABS(xAmount); assert(xSize <= 80); @@ -817,15 +929,32 @@ void M4Surface::translate(RGBList *list, bool isTransparent) { byte *palIndexes = list->palIndexes(); for (int i = 0; i < width() * height(); ++i, ++p) { - if (!isTransparent || (*p != 0)) { - assert(*p < list->size()); - *p = palIndexes[*p]; + if (!isTransparent || (*p != TRANSPARENT_COLOUR_INDEX)) { + if (*p < list->size()) + *p = palIndexes[*p]; + else + warning("Pal index %d exceeds list size %d", *p, list->size()); } } freeData(); } +M4Surface *M4Surface::flipHorizontal() const { + M4Surface *dest = new M4Surface(width(), height()); + dest->_rgbList = (this->_rgbList == NULL) ? NULL : this->_rgbList->clone(); + + byte *destP = dest->getBasePtr(); + + for (int y = 0; y < height(); ++y) { + const byte *srcP = getBasePtr(width() - 1, y); + for (int x = 0; x < width(); ++x) + *destP++ = *srcP--; + } + + return dest; +} + //-------------------------------------------------------------------------- // Palette class // |