From 3e05d9008e88b108a55455643900939f91174670 Mon Sep 17 00:00:00 2001 From: Paul Gilbert Date: Sun, 22 Feb 2015 20:37:56 -0500 Subject: XEEN: Merge scaling code from XSurface into sprite drawing code --- engines/xeen/sprites.cpp | 183 ++++++++++++++++++++++++++++++----------------- 1 file changed, 119 insertions(+), 64 deletions(-) (limited to 'engines/xeen/sprites.cpp') diff --git a/engines/xeen/sprites.cpp b/engines/xeen/sprites.cpp index 93c7e426f1..d75eb9cb4f 100644 --- a/engines/xeen/sprites.cpp +++ b/engines/xeen/sprites.cpp @@ -33,10 +33,14 @@ namespace Xeen { SpriteResource::SpriteResource() { _filesize = 0; _data = nullptr; + _scaledWidth = _scaledHeight = 0; + Common::fill(&_lineDist[0], &_lineDist[SCREEN_WIDTH], false); } SpriteResource::SpriteResource(const Common::String &filename) { _data = nullptr; + _scaledWidth = _scaledHeight = 0; + Common::fill(&_lineDist[0], &_lineDist[SCREEN_WIDTH], false); load(filename); } @@ -112,7 +116,7 @@ void SpriteResource::clear() { * Draws a frame using data at a specific offset in the sprite resource */ void SpriteResource::drawOffset(XSurface &dest, uint16 offset, const Common::Point &destPos, - const Common::Rect &bounds, int flags) const { + const Common::Rect &bounds, int flags, int scale) { // Get cell header Common::MemoryReadStream f(_data, _filesize); f.seek(offset); @@ -121,6 +125,14 @@ void SpriteResource::drawOffset(XSurface &dest, uint16 offset, const Common::Poi int yOffset = f.readUint16LE(); int height = f.readUint16LE(); + // TODO: I don't currently know the algorithm the original used for scaling. + // This is a best fit estimate that every increment of the scale field + // reduces the size of a sprite by approximately 6.6% + int newScale = MAX(100.0 - 6.6 * scale, 0.0); + if (newScale == 0) + return; + setupScaling(newScale, xOffset + width, yOffset + height); + bool flipped = (flags & SPRFLAG_HORIZ_FLIPPED) != 0; int xInc = flipped ? -1 : 1; @@ -134,24 +146,34 @@ void SpriteResource::drawOffset(XSurface &dest, uint16 offset, const Common::Poi // Main loop int opr1, opr2; int32 pos; - for (int yPos = yOffset, byteCount = 0; yPos < height + yOffset; yPos++, byteCount = 0) { + int yIndex = yOffset; + int yPos = destPos.y + getScaledValue(yOffset); + for (int yCtr = 0, byteCount = 0; yCtr < height; ++yCtr, ++yIndex, byteCount = 0) { // The number of bytes in this scan line int lineLength = f.readByte(); if (lineLength == 0) { // Skip the specified number of scan lines - yPos += f.readByte(); - } else if ((destPos.y + yPos) < bounds.top || (destPos.y + yPos) >= bounds.bottom) { + int numLines = f.readByte(); + for (int idx = 0; idx < numLines; ++idx, ++yIndex, ++yCtr) { + if (_lineDist[yIndex]) + ++yPos; + } + } else if (destPos.y < bounds.top || yPos >= bounds.bottom + || !_lineDist[yIndex]) { // Skip over the bytes of the line f.skip(lineLength); } else { + const byte *lineStartP = (const byte *)dest.getBasePtr(bounds.left, yPos); + const byte *lineEndP = (const byte *)dest.getBasePtr(bounds.right, yPos); + // Skip the transparent pixels at the beginning of the scan line int xPos = f.readByte() + xOffset; ++byteCount; - const byte *lineStartP = (const byte *)dest.getBasePtr(bounds.left, destPos.y + yPos); - const byte *lineEndP = (const byte *)dest.getBasePtr(bounds.right, destPos.y + yPos); - byte *destP = !flipped ? - (byte *)dest.getBasePtr(destPos.x + xPos, destPos.y + yPos) : - (byte *)dest.getBasePtr(destPos.x + xOffset + width - xPos, destPos.y + yPos); + int xAmt = getScaledValue(flipped ? xOffset + width - xPos : xPos); + int xIndex = 0; + + byte *destP = (byte *)dest.getBasePtr(destPos.x + xAmt, yPos); + ++yPos; while (byteCount < lineLength) { // The next byte is an opcode that determines what @@ -169,18 +191,22 @@ void SpriteResource::drawOffset(XSurface &dest, uint16 offset, const Common::Poi byte b = f.readByte(); ++byteCount; - if (destP >= lineStartP && destP < lineEndP) - *destP = b; - destP += xInc; + if (_lineDist[xIndex++]) { + if (destP >= lineStartP && destP < lineEndP) + *destP = b; + destP += xInc; + } } break; case 2: // The following byte is an index into the color table, draw it len + 3 times. opr1 = f.readByte(); ++byteCount; for (int i = 0; i < len + 3; ++i, ++xPos) { - if (destP >= lineStartP && destP < lineEndP) - *destP = opr1; - destP += xInc; + if (_lineDist[xIndex++]) { + if (destP >= lineStartP && destP < lineEndP) + *destP = opr1; + destP += xInc; + } } break; @@ -191,9 +217,11 @@ void SpriteResource::drawOffset(XSurface &dest, uint16 offset, const Common::Poi for (int i = 0; i < len + 4; ++i, ++xPos) { byte b = f.readByte(); - if (destP >= lineStartP && destP < lineEndP) - *destP = b; - destP += xInc; + if (_lineDist[xIndex++]) { + if (destP >= lineStartP && destP < lineEndP) + *destP = b; + destP += xInc; + } } f.seek(pos, SEEK_SET); @@ -204,19 +232,29 @@ void SpriteResource::drawOffset(XSurface &dest, uint16 offset, const Common::Poi opr2 = f.readByte(); ++byteCount; for (int i = 0; i < len + 2; ++i, xPos += 2) { if (destP < lineStartP || destP >= (lineEndP - 1)) { - destP += 2 * xInc; + if (_lineDist[xIndex++]) + destP += xInc; + if (_lineDist[xIndex++]) + destP += xInc; } else { - *destP = opr1; - destP += xInc; - *destP = opr2; - destP += xInc; + if (_lineDist[xIndex++]) { + *destP = opr1; + destP += xInc; + } + if (_lineDist[xIndex++]) { + *destP = opr2; + destP += xInc; + } } } break; case 5: // Skip len + 1 pixels filling them with the transparent color. - xPos += len + 1; - destP += (len + 1) * xInc; + for (int idx = 0; idx < (len + 1); ++idx) { + if (_lineDist[xIndex++]) + destP += xInc; + ++xPos; + } break; case 6: // Pattern command. @@ -227,9 +265,11 @@ void SpriteResource::drawOffset(XSurface &dest, uint16 offset, const Common::Poi opr1 = f.readByte(); ++byteCount; for (int i = 0; i < len + 3; ++i, ++xPos) { - if (destP >= lineStartP && destP < lineEndP) - *destP = opr1; - destP += xInc; + if (_lineDist[xIndex++]) { + if (destP >= lineStartP && destP < lineEndP) + *destP = opr1; + destP += xInc; + } opr1 += patternSteps[cmd + (i % 2)]; } break; @@ -242,20 +282,21 @@ void SpriteResource::drawOffset(XSurface &dest, uint16 offset, const Common::Poi } } - Common::Rect r(Common::Rect(destPos.x + xOffset, destPos.y + yOffset, - destPos.x + xOffset + width, destPos.y + yOffset + height)); + Common::Rect r(Common::Rect( + destPos.x + getScaledValue(xOffset), destPos.y + getScaledValue(yOffset), + destPos.x + getScaledValue(xOffset + width), destPos.y + getScaledValue(yOffset + height))); r.clip(Common::Rect(0, 0, dest.w, dest.h)); if (!r.isEmpty()) dest.addDirtyRect(r); } void SpriteResource::draw(XSurface &dest, int frame, const Common::Point &destPos, - int flags, int scale) const { + int flags, int scale) { draw(dest, frame, destPos, Common::Rect(0, 0, dest.w, dest.h), flags, scale); } void SpriteResource::draw(Window &dest, int frame, const Common::Point &destPos, - int flags, int scale) const { + int flags, int scale) { draw(dest, frame, destPos, dest.getBounds(), flags, scale); } @@ -263,44 +304,58 @@ void SpriteResource::draw(Window &dest, int frame, const Common::Point &destPos, * Draw the sprite onto the given surface */ void SpriteResource::draw(XSurface &dest, int frame, const Common::Point &destPos, - const Common::Rect &bounds, int flags, int scale) const { - assert(scale != 0x8000); // TODO: TO test when I find scale value used - - if (scale == 0) { - drawOffset(dest, _index[frame]._offset1, destPos, bounds, flags); - if (_index[frame]._offset2) - drawOffset(dest, _index[frame]._offset2, destPos, bounds, flags); - } else { - // Get the bounds for the surface and create a temporary one - Common::MemoryReadStream f(_data, _filesize); - f.seek(_index[frame]._offset1); - int xOffset = f.readUint16LE(); - int width = f.readUint16LE(); - int yOffset = f.readUint16LE(); - int height = f.readUint16LE(); - XSurface tempSurface(xOffset + width, yOffset + height); - Common::Rect tempBounds(0, 0, tempSurface.w, tempSurface.h); - - // Draw sprite into temporary surface - tempSurface.fillRect(Common::Rect(0, 0, width, height), 0); - drawOffset(tempSurface, _index[frame]._offset1, Common::Point(), tempBounds, flags); - if (_index[frame]._offset2) - drawOffset(tempSurface, _index[frame]._offset2, Common::Point(), tempBounds, flags); - - // TODO: I don't currently know the algorithm the original used for scaling. - // This is a best fit estimate that every increment of the scale field - // reduces the size of a sprite by approximately 6.6% - int newScale = MAX(100.0 - 6.6 * scale, 0.0); - if (newScale > 0) - tempSurface.transBlitTo(dest, Common::Point(), newScale, 0); - } + const Common::Rect &bounds, int flags, int scale) { + // TODO: TO test when I find sprites using scale values and flags + assert(scale != 0x8000); + assert(scale >= 0); +// assert((flags & SPRFLAG_2000) == 0); + + drawOffset(dest, _index[frame]._offset1, destPos, bounds, flags, scale); + if (_index[frame]._offset2) + drawOffset(dest, _index[frame]._offset2, destPos, bounds, flags, scale); } /** * Draw the sprite onto the given surface */ -void SpriteResource::draw(XSurface &dest, int frame) const { +void SpriteResource::draw(XSurface &dest, int frame) { draw(dest, frame, Common::Point()); } +void SpriteResource::setupScaling(int scale, int frameWidth, int frameHeight) { + int highestDim = MAX(frameWidth, frameHeight); + int distCtr = 0; + int distIndex = 0; + _scaledWidth = _scaledHeight = 0; + + do { + distCtr += scale; + if (distCtr < 100) { + _lineDist[distIndex] = false; + } else { + _lineDist[distIndex] = true; + distCtr -= 100; + + if (distIndex < frameWidth) + ++_scaledWidth; + + if (distIndex < frameHeight) + ++_scaledHeight; + } + } while (++distIndex < highestDim); +} + +/** + * Returns a scaled value based on a passed in x or y distance + */ +int SpriteResource::getScaledValue(int xy) { + int newVal = 0; + for (int idx = 0; idx < xy; ++idx) { + if (_lineDist[idx]) + ++newVal; + } + + return newVal; +} + } // End of namespace Xeen -- cgit v1.2.3