From 5660ce81340a013c0c653a55b7430737213783a4 Mon Sep 17 00:00:00 2001 From: Matthew Stewart Date: Thu, 26 Jul 2018 02:25:18 -0400 Subject: STARTREK: Fix .BAN file rendering behind textboxes --- engines/startrek/graphics.cpp | 11 ++++---- engines/startrek/graphics.h | 12 +++++++-- engines/startrek/startrek.cpp | 58 ++++++++++++++++++++++++++++++++++++------- 3 files changed, 64 insertions(+), 17 deletions(-) diff --git a/engines/startrek/graphics.cpp b/engines/startrek/graphics.cpp index 43358f8c1c..cce9861934 100644 --- a/engines/startrek/graphics.cpp +++ b/engines/startrek/graphics.cpp @@ -304,14 +304,14 @@ void Graphics::drawSprite(const Sprite &sprite, ::Graphics::Surface *surface) { // rect is the portion of the sprite to update. It must be entirely contained within the // sprite's actual, full rectangle. -void Graphics::drawSprite(const Sprite &sprite, ::Graphics::Surface *surface, const Common::Rect &rect) { +void Graphics::drawSprite(const Sprite &sprite, ::Graphics::Surface *surface, const Common::Rect &rect, int rectLeft, int rectTop) { Common::Rect spriteRect = Common::Rect(sprite.drawX, sprite.drawY, sprite.drawX + sprite.bitmap->width, sprite.drawY + sprite.bitmap->height); assert(_screenRect.contains(rect)); assert(spriteRect.contains(rect)); - byte *dest = (byte *)surface->getPixels() + rect.top * SCREEN_WIDTH + rect.left; + byte *dest = (byte *)surface->getPixels() + (rect.top - rectTop) * SCREEN_WIDTH + (rect.left - rectLeft); switch (sprite.drawMode) { case 0: { // Normal sprite @@ -583,8 +583,9 @@ void Graphics::drawAllSprites(bool updateScreen) { this->updateScreen(); } -void Graphics::drawAllSpritesInRect(const Common::Rect &rect) { - ::Graphics::Surface *surface = _vm->_system->lockScreen(); +void Graphics::drawAllSpritesInRectToSurface(const Common::Rect &rect, ::Graphics::Surface *surface) { + surface->copyFrom(*_vm->_system->lockScreen()); + _vm->_system->unlockScreen(); for (int i = 0; i < _numSprites; i++) { Sprite *sprite = _sprites[i]; @@ -595,8 +596,6 @@ void Graphics::drawAllSpritesInRect(const Common::Rect &rect) { if (!intersect.isEmpty()) drawSprite(*sprite, surface, intersect); } - - _vm->_system->unlockScreen(); } void Graphics::forceDrawAllSprites(bool updateScreen) { diff --git a/engines/startrek/graphics.h b/engines/startrek/graphics.h index 1a71aa29b3..bd5784e3a2 100644 --- a/engines/startrek/graphics.h +++ b/engines/startrek/graphics.h @@ -105,13 +105,21 @@ public: void drawTextChar(::Graphics::Surface *surface, const Sprite &sprite, int x, int y, const Common::Rect &rect); void drawSprite(const Sprite &sprite, ::Graphics::Surface *surface); - void drawSprite(const Sprite &sprite, ::Graphics::Surface *surface, const Common::Rect &rect); + /** + * @param sprite The sprite to draw + * @param surface The surface to draw to + * @param rect The part of the sprite to draw (only draw the part of the sprite that + * intersects with it) + @ @param rectLeft X-offset to subtract before drawing to surface. + @ @param rectTop Y-offset to subtract before drawing to surface. + */ + void drawSprite(const Sprite &sprite, ::Graphics::Surface *surface, const Common::Rect &rect, int rectLeft = 0, int rectTop = 0); void drawAllSprites(bool updateScreen = true); /** * This function should only be called after "drawAllSprites" (so that sprite rects * are updated). */ - void drawAllSpritesInRect(const Common::Rect &rect); + void drawAllSpritesInRectToSurface(const Common::Rect &rect, ::Graphics::Surface *surface); /** * Sets "bitmapChanged" to true on all sprites before calling drawAllSprites. */ diff --git a/engines/startrek/startrek.cpp b/engines/startrek/startrek.cpp index 4703bbebb4..9623ccccc8 100644 --- a/engines/startrek/startrek.cpp +++ b/engines/startrek/startrek.cpp @@ -884,23 +884,63 @@ void StarTrekEngine::renderBanAboveSprites() { rect.top = _banFiles[i]->readSint16(); rect.right = _banFiles[i]->readSint16() + 1; rect.bottom = _banFiles[i]->readSint16() + 1; - _gfx->drawAllSpritesInRect(rect); - // Just read through the BAN data to get the end address, not doing anything - // with it. + // Draw all sprites in this rectangle to a custom surface, and only update the + // specific pixels that were updated by the BAN file this frame. + // Rationale behind this is that, since the background may not have been + // redrawn, the transparent sprites (ie. textboxes) would further darken any + // pixels behind them that haven't been updated this frame. So, we can't just + // update everything in this rectangle. + // FIXME: This copies the entire screen surface for temporary drawing, which + // is somewhat wasteful. Original game had one more graphics layer it drew to + // before the screen was updated... + ::Graphics::Surface surface; + _gfx->drawAllSpritesInRectToSurface(rect, &surface); + + byte *destPixels = _gfx->lockScreenPixels(); + byte *src = (byte *)surface.getPixels() + offset; + byte *dest = destPixels + offset; + + /* + _banFiles[i]->seek(_banFileOffsets[i], SEEK_SET); + renderBan(destPixels, _banFiles[i]); + */ + // This is similar to renderBan(), except it copies pixels from the surface + // above instead of drawing directly to it. (Important since sprites may be + // drawn on top.) while (--size >= 0) { + assert(dest >= destPixels && dest < destPixels + SCREEN_WIDTH * SCREEN_HEIGHT); int8 b = _banFiles[i]->readByte(); - if (b == -128) - _banFiles[i]->readUint16(); - else if (b < 0) { - _banFiles[i]->readByte(); + if (b == -128) { + uint16 skip = _banFiles[i]->readUint16(); + dest += skip; + src += skip; + } else if (b < 0) { + byte c = _banFiles[i]->readByte(); + if (c == 0) { + dest += (-b) + 1; + src += (-b) + 1; + } + else { + for (int j = 0; j < (-b) + 1; j++) + *(dest++) = *(src++); + } } else { b++; - while (b-- != 0) - _banFiles[i]->readByte(); + while (b-- != 0) { + byte c = _banFiles[i]->readByte(); + if (c == 0) { + dest++; + src++; + } else + *(dest++) = *(src++); + } } } + _gfx->unlockScreenPixels(); + surface.free(); + _banFileOffsets[i] = _banFiles[i]->pos(); } } -- cgit v1.2.3