diff options
-rw-r--r-- | engines/mads/messages.cpp | 20 | ||||
-rw-r--r-- | engines/mads/messages.h | 23 | ||||
-rw-r--r-- | engines/mads/msurface.cpp | 176 | ||||
-rw-r--r-- | engines/mads/msurface.h | 21 | ||||
-rw-r--r-- | engines/mads/scene.cpp | 20 | ||||
-rw-r--r-- | engines/mads/sprites.cpp | 89 | ||||
-rw-r--r-- | engines/mads/sprites.h | 7 |
7 files changed, 332 insertions, 24 deletions
diff --git a/engines/mads/messages.cpp b/engines/mads/messages.cpp index 3b6663e71d..11db00f1b9 100644 --- a/engines/mads/messages.cpp +++ b/engines/mads/messages.cpp @@ -305,7 +305,7 @@ TextDisplayList::TextDisplayList(MADSEngine *vm) : _vm(vm) { } } -void TextDisplayList::clear() { +void TextDisplayList::reset() { for (int i = 0; i < TEXT_DISPLAY_SIZE; ++i) (*this)[i]._active = false; } @@ -361,18 +361,16 @@ void TextDisplayList::setDirtyAreas2() { } } -void TextDisplayList::draw(MSurface *view) { - error("TODO"); - /* - for (uint idx = 0; idx < _entries.size(); ++idx) { - if (_entries[idx]._active && (_entries[idx]._expire >= 0)) { - _entries[idx]._font->setColors(_entries[idx]._color1, _entries[idx]._color2, 0); - _entries[idx]._font->writeString(view, _entries[idx]._msg, - Common::Point(_entries[idx]._bounds.left, _entries[idx]._bounds.top), - _entries[idx]._bounds.width(), _entries[idx]._spacing); +void TextDisplayList::draw(MSurface *s) { + for (uint idx = 0; idx < size(); ++idx) { + TextDisplay &td = (*this)[idx]; + if (td._active && (td._expire >= 0)) { + td._font->setColors(0xFF, td._color1, td._color2, 0); + td._font->writeString(s, td._msg, + Common::Point(td._bounds.left, td._bounds.top), + td._bounds.width(), td._spacing); } } - */ } void TextDisplayList::cleanUp() { diff --git a/engines/mads/messages.h b/engines/mads/messages.h index 1357b98d03..19a0115f94 100644 --- a/engines/mads/messages.h +++ b/engines/mads/messages.h @@ -108,11 +108,6 @@ public: class TextDisplayList: public Common::Array<TextDisplay> { private: MADSEngine *_vm; - - /** - * Determine dirty areas for active text areas - */ - void setDirtyAreas2(); public: TextDisplayList(MADSEngine *vm); @@ -122,8 +117,17 @@ public: void expire(int idx); int add(int xp, int yp, uint fontColor, int charSpacing, const Common::String &, Font *font); - void clear(); - void draw(MSurface *view); + + /** + * Reset all of the text display entries in the list to inactive + */ + void reset(); + + /** + * Draw any text in the list to the specified surface + * @param surface Surface + */ + void draw(MSurface *s); /** * Determine dirty areas for active text areas @@ -131,6 +135,11 @@ public: void setDirtyAreas(); /** + * Secondary setup dirty areas for the text areas + */ + void setDirtyAreas2(); + + /** * Deactivates any text display entries that are finished */ void cleanUp(); diff --git a/engines/mads/msurface.cpp b/engines/mads/msurface.cpp index 5f7b37b1cd..aed00f5553 100644 --- a/engines/mads/msurface.cpp +++ b/engines/mads/msurface.cpp @@ -244,6 +244,168 @@ void MSurface::copyFrom(MSurface *src, const Common::Rect &srcBounds, } } +void MSurface::copyFrom(MSurface *src, const Common::Point &destPos, int depth, + MSurface *depthsSurface, 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 *depthsData = depthsSurface->getData(); + byte *depthsPtr = depthsData + (depthsSurface->pitch * destY) + destX; + 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) { + if ((depth <= (depthsPtr[xCtr] & 0x7f)) && (srcPtr[xCtr] != transparentColor)) + destPtr[xCtr] = srcPtr[xCtr]; + } + + srcPtr += src->getWidth(); + depthsPtr += depthsSurface->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; // TODO: false for negative frame numbers + 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); + 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 != transparentColor) && (depth <= (*depthP & 0x7f))) + *destP = *srcP; + + ++destP; + ++depthP; + } + + // Move to the next destination line + destPixelsP += this->pitch; + depthPixelsP += depthsSurface->pitch; + } +} + void MSurface::translate(Common::Array<RGB6> &palette) { for (int y = 0; y < this->h; ++y) { byte *pDest = getBasePtr(0, y); @@ -254,4 +416,18 @@ void MSurface::translate(Common::Array<RGB6> &palette) { } } +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->getData(); + + for (int x = 0; x < this->w; ++x) + *destP++ = *srcP--; + } + + return dest; +} + } // End of namespace MADS diff --git a/engines/mads/msurface.h b/engines/mads/msurface.h index 7043cd4eff..c797033859 100644 --- a/engines/mads/msurface.h +++ b/engines/mads/msurface.h @@ -127,6 +127,11 @@ public: byte *getBasePtr(int x, int y) { return (byte *)Graphics::Surface::getBasePtr(x, y); } /** + * Returns a pointer to a given position within the surface + */ + const byte *getBasePtr(int x, int y) const { return (const byte *)Graphics::Surface::getBasePtr(x, y); } + + /** * Clears the surface */ void empty(); @@ -142,6 +147,17 @@ public: int transparentColor = -1); /** + * Copys a sub-section of another surface into the current one. + * @param src Source surface + * @param destPos Destination position to draw in current surface + * @param depth Depth of sprite + * @param depthSurface Depth surface to use with sprite depth + * @param transparentColor Transparency palette index + */ + void copyFrom(MSurface *src, const Common::Point &destPos, int depth, MSurface *depthSurface, + int scale, int transparentColor = -1); + + /** * Copies the surface to a given destination surface */ void copyTo(MSurface *dest, int transparentColor = -1) { @@ -167,6 +183,11 @@ public: * Translates the pixels of an image used the passed palette with RGB mapping */ void translate(Common::Array<RGB6> &palette); + + /** + * Create a new surface which is a flipped horizontal copy of the current one + */ + MSurface *flipHorizontal() const; }; } // End of namespace MADS diff --git a/engines/mads/scene.cpp b/engines/mads/scene.cpp index 1d489ddb2d..7359562180 100644 --- a/engines/mads/scene.cpp +++ b/engines/mads/scene.cpp @@ -423,18 +423,30 @@ void Scene::doFrame() { } void Scene::drawElements(bool transitionFlag, bool surfaceFlag) { - // Draw any background objects + // Draw any sprites + _dirtyAreas.clear(); _spriteSlots.drawBackground(); + // Process dirty areas + _textDisplay.setDirtyAreas(); + // Merge any identified dirty areas - _dirtyAreas.merge(1, _dirtyAreas.size()); + _dirtyAreas.merge(1, DIRTY_AREAS_SIZE); // Copy dirty areas to the main display surface _dirtyAreas.copy(&_vm->_screen, &_backgroundSurface, _posAdjust); - // Set dirty areas + // Handle dirty areas for foreground objects _spriteSlots.setDirtyAreas(); - _textDisplay.setDirtyAreas(); + _textDisplay.setDirtyAreas2(); + _dirtyAreas.merge(1, DIRTY_AREAS_SIZE); + + + // Draw foreground sprites + _spriteSlots.drawForeground(&_vm->_screen); + + // Draw text elements onto the view + _textDisplay.draw(&_vm->_screen); // _vm->_screen.setPointer(&_vm->_screen); diff --git a/engines/mads/sprites.cpp b/engines/mads/sprites.cpp index e7349f6c60..aa15f665f4 100644 --- a/engines/mads/sprites.cpp +++ b/engines/mads/sprites.cpp @@ -36,6 +36,24 @@ enum { kMarker = 2 }; +#define TRANSPARENT_COLOR_INDEX 0xFF + +class DepthEntry { +public: + int depth; + int index; + + DepthEntry(int depthAmt, int indexVal) { depth = depthAmt; index = indexVal; } +}; + +bool sortHelper(const DepthEntry &entry1, const DepthEntry &entry2) { + return entry1.depth < entry2.depth; +} + +typedef Common::List<DepthEntry> DepthList; + +/*------------------------------------------------------------------------*/ + MSprite::MSprite(): MSurface() { _encoding = 0; } @@ -105,6 +123,10 @@ void MSprite::loadSprite(Common::SeekableReadStream *source) { } } +byte MSprite::getTransparencyIndex() const { + return TRANSPARENT_COLOR_INDEX; +} + /*------------------------------------------------------------------------*/ MADSEngine *SpriteSlot::_vm = nullptr; @@ -233,6 +255,71 @@ void SpriteSlots::drawBackground() { } } +void SpriteSlots::drawForeground(MSurface *s) { + DepthList depthList; + Scene &scene = _vm->_game->_scene; + + // Get a list of sprite object depths for active objects + for (uint i = 0; i < size(); ++i) { + if ((*this)[i]._spriteType >= ST_NONE) { + DepthEntry rec(16 - (*this)[i]._depth, i); + depthList.push_back(rec); + } + } + + // Sort the list in order of the depth + Common::sort(depthList.begin(), depthList.end(), sortHelper); + + // Loop through each of the objects + DepthList::iterator i; + for (i = depthList.begin(); i != depthList.end(); ++i) { + DepthEntry &de = *i; + SpriteSlot &slot = (*this)[de.index]; + assert(slot._spritesIndex < (int)scene._sprites.size()); + SpriteAsset &spriteSet = *scene._sprites[slot._spritesIndex]; + + // Get the sprite frame + int frameNumber = slot._frameNumber & 0x7fff; + bool flipped = (slot._frameNumber & 0x8000) != 0; + MSprite *sprite = spriteSet.getFrame(frameNumber - 1); + + MSurface *spr = sprite; + if (flipped) { + // Create a flipped copy of the sprite temporarily + spr = sprite->flipHorizontal(); + } + + if ((slot._scale < 100) && (slot._scale != -1)) { + // Minimalised drawing + s->copyFrom(spr, slot._position, slot._depth, &scene._depthSurface, + slot._scale, sprite->getTransparencyIndex()); + } else { + int xp, yp; + + if (slot._scale == -1) { + xp = slot._position.x - scene._posAdjust.x; + yp = slot._position.y - scene._posAdjust.y; + } else { + xp = slot._position.x - (spr->w / 2) - scene._posAdjust.x; + yp = slot._position.y - spr->h - scene._posAdjust.y + 1; + } + + if (slot._depth > 1) { + // Draw the frame with depth processing + s->copyFrom(spr, Common::Point(xp, yp), slot._depth, &scene._depthSurface, + 100, sprite->getTransparencyIndex()); + } else { + // No depth, so simply draw the image + spr->copyTo(s, Common::Point(xp, yp), sprite->getTransparencyIndex()); + } + } + + // Free sprite if it was a flipped one + if (flipped) + delete spr; + } +} + void SpriteSlots::cleanUp() { for (int i = (int)size() - 1; i >= 0; --i) { if ((*this)[i]._spriteType >= ST_NONE) @@ -254,6 +341,4 @@ int SpriteSets::add(SpriteAsset *asset, int idx) { return idx; } -/*------------------------------------------------------------------------*/ - } // End of namespace MADS diff --git a/engines/mads/sprites.h b/engines/mads/sprites.h index b1208d0df5..f9ae46ad9b 100644 --- a/engines/mads/sprites.h +++ b/engines/mads/sprites.h @@ -114,6 +114,8 @@ public: Common::Point _pos; Common::Point _offset; uint8 _encoding; + + byte getTransparencyIndex() const; }; class SpriteSlotSubset { @@ -188,6 +190,11 @@ public: */ void drawBackground(); + /** + * Draw any sprites into the foreground of the scene + */ + void drawForeground(MSurface *s); + void cleanUp(); }; |