diff options
-rw-r--r-- | engines/xeen/sprites.cpp | 183 | ||||
-rw-r--r-- | engines/xeen/sprites.h | 16 | ||||
-rw-r--r-- | engines/xeen/xsurface.cpp | 142 | ||||
-rw-r--r-- | engines/xeen/xsurface.h | 7 |
4 files changed, 130 insertions, 218 deletions
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 diff --git a/engines/xeen/sprites.h b/engines/xeen/sprites.h index 7fc6793499..2d9fe2f8c6 100644 --- a/engines/xeen/sprites.h +++ b/engines/xeen/sprites.h @@ -45,14 +45,20 @@ private: Common::Array<IndexEntry> _index; int32 _filesize; byte *_data; + bool _lineDist[320]; + int _scaledWidth, _scaledHeight; void load(Common::SeekableReadStream &f); void draw(XSurface &dest, int frame, const Common::Point &destPos, - const Common::Rect &bounds, int flags = 0, int scale = 0) const; + const Common::Rect &bounds, int flags = 0, int scale = 0); void drawOffset(XSurface &dest, uint16 offset, const Common::Point &destPos, - const Common::Rect &bounds, int flags) const; + const Common::Rect &bounds, int flags, int scale); + + void setupScaling(int scale, int frameWidth, int frameHeight); + + int getScaledValue(int xy); public: SpriteResource(); SpriteResource(const Common::String &filename); @@ -68,12 +74,12 @@ public: void clear(); void draw(XSurface &dest, int frame, const Common::Point &destPos, - int flags = 0, int scale = 0) const; + int flags = 0, int scale = 0); void draw(Window &dest, int frame, const Common::Point &destPos, - int flags = 0, int scale = 0) const; + int flags = 0, int scale = 0); - void draw(XSurface &dest, int frame) const; + void draw(XSurface &dest, int frame); int size() const { return _index.size(); } diff --git a/engines/xeen/xsurface.cpp b/engines/xeen/xsurface.cpp index 7060a6326a..04a264a80c 100644 --- a/engines/xeen/xsurface.cpp +++ b/engines/xeen/xsurface.cpp @@ -55,152 +55,10 @@ void XSurface::create(XSurface *s, const Common::Rect &bounds) { _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); diff --git a/engines/xeen/xsurface.h b/engines/xeen/xsurface.h index 61f4f96cb1..d8747d4372 100644 --- a/engines/xeen/xsurface.h +++ b/engines/xeen/xsurface.h @@ -44,13 +44,6 @@ public: void create(XSurface *s, const Common::Rect &bounds); - void transBlitTo(XSurface &dest) const; - - void transBlitTo(XSurface &dest, const Common::Point &destPos) const; - - void transBlitTo(XSurface &dest, const Common::Point &destPos, - int scale, int transparentColor); - void blitTo(XSurface &dest, const Common::Point &destPos) const; void blitTo(XSurface &dest) const; |