From 487a86fe7dc9904f445d91667095f641f72f7c81 Mon Sep 17 00:00:00 2001 From: Jared Adams Date: Thu, 20 May 2010 01:35:17 -0600 Subject: Buffer layered sprites under SDL This improves framerate and allows transparent overlay for complex sprites. Two copies of the buffer are kept, one at full opacity, one with variable opactiy, to reduce calls to setAlpha. Reviewed-by: Bertram --- src/actorsprite.cpp | 6 +- src/animatedsprite.cpp | 34 ++++++-- src/animatedsprite.h | 10 +-- src/being.cpp | 12 +-- src/compoundsprite.cpp | 221 ++++++++++++++++++++++++++++++++++++++++++++----- src/compoundsprite.h | 50 ++++++++--- src/graphics.cpp | 10 ++- src/graphics.h | 12 +++ src/imagesprite.h | 14 ++-- src/sprite.h | 20 +++-- 10 files changed, 320 insertions(+), 69 deletions(-) diff --git a/src/actorsprite.cpp b/src/actorsprite.cpp index da81873f..92894b04 100644 --- a/src/actorsprite.cpp +++ b/src/actorsprite.cpp @@ -304,14 +304,14 @@ void ActorSprite::setupSpriteDisplay(const SpriteDisplay &display, { std::string file = "graphics/sprites/" + (*it)->sprite; int variant = (*it)->variant; - push_back(AnimatedSprite::load(file, variant)); + addSprite(AnimatedSprite::load(file, variant)); } // Ensure that something is shown, if desired if (size() == 0 && forceDisplay) { if (display.image.empty()) - push_back(AnimatedSprite::load("graphics/sprites/error.xml")); + addSprite(AnimatedSprite::load("graphics/sprites/error.xml")); else { ResourceManager *resman = ResourceManager::getInstance(); @@ -321,7 +321,7 @@ void ActorSprite::setupSpriteDisplay(const SpriteDisplay &display, if (!img) img = Theme::getImageFromTheme("unknown-item.png"); - push_back(new ImageSprite(img)); + addSprite(new ImageSprite(img)); } } diff --git a/src/animatedsprite.cpp b/src/animatedsprite.cpp index bc595f79..3e4104a9 100644 --- a/src/animatedsprite.cpp +++ b/src/animatedsprite.cpp @@ -70,18 +70,22 @@ AnimatedSprite::~AnimatedSprite() mSprite->decRef(); } -void AnimatedSprite::reset() +bool AnimatedSprite::reset() { + bool ret = mFrameIndex !=0 || mFrameTime != 0 || mLastTime != 0; + mFrameIndex = 0; mFrameTime = 0; mLastTime = 0; + + return ret; } -void AnimatedSprite::play(SpriteAction spriteAction) +bool AnimatedSprite::play(SpriteAction spriteAction) { Action *action = mSprite->getAction(spriteAction); if (!action) - return; + return false; mAction = action; Animation *animation = mAction->getAnimation(mDirection); @@ -92,10 +96,14 @@ void AnimatedSprite::play(SpriteAction spriteAction) mFrame = mAnimation->getFrame(0); reset(); + + return true; } + + return false; } -void AnimatedSprite::update(int time) +bool AnimatedSprite::update(int time) { // Avoid freaking out at first frame or when tick_time overflows if (time < mLastTime || mLastTime == 0) @@ -103,16 +111,22 @@ void AnimatedSprite::update(int time) // If not enough time has passed yet, do nothing if (time <= mLastTime || !mAnimation) - return; + return false; unsigned int dt = time - mLastTime; mLastTime = time; + Animation *animation = mAnimation; + Frame *frame = mFrame; + if (!updateCurrentAnimation(dt)) { // Animation finished, reset to default play(ACTION_STAND); } + + // Make sure something actually changed + return animation != mAnimation || frame != mFrame; } bool AnimatedSprite::updateCurrentAnimation(unsigned int time) @@ -159,14 +173,14 @@ bool AnimatedSprite::draw(Graphics *graphics, int posX, int posY) const posY + mFrame->offsetY); } -void AnimatedSprite::setDirection(SpriteDirection direction) +bool AnimatedSprite::setDirection(SpriteDirection direction) { if (mDirection != direction) { mDirection = direction; if (!mAction) - return; + return false; Animation *animation = mAction->getAnimation(mDirection); @@ -176,7 +190,11 @@ void AnimatedSprite::setDirection(SpriteDirection direction) mFrame = mAnimation->getFrame(0); reset(); } + + return true; } + + return false; } int AnimatedSprite::getWidth() const @@ -195,7 +213,7 @@ int AnimatedSprite::getHeight() const return 0; } -Image* AnimatedSprite::getImage() const +const Image* AnimatedSprite::getImage() const { return mFrame ? mFrame->image : 0; } diff --git a/src/animatedsprite.h b/src/animatedsprite.h index 833a07a3..aae116be 100644 --- a/src/animatedsprite.h +++ b/src/animatedsprite.h @@ -54,11 +54,11 @@ class AnimatedSprite : public Sprite virtual ~AnimatedSprite(); - void reset(); + bool reset(); - void play(SpriteAction action); + bool play(SpriteAction action); - void update(int time); + bool update(int time); bool draw(Graphics* graphics, int posX, int posY) const; @@ -66,9 +66,9 @@ class AnimatedSprite : public Sprite int getHeight() const; - Image* getImage() const; + const Image* getImage() const; - void setDirection(SpriteDirection direction); + bool setDirection(SpriteDirection direction); int getNumberOfLayers() { return 1; } diff --git a/src/being.cpp b/src/being.cpp index a2180c84..24f2e2e1 100644 --- a/src/being.cpp +++ b/src/being.cpp @@ -1063,7 +1063,7 @@ void Being::setSprite(unsigned int slot, int id, const std::string &color, assert(slot < Net::getCharHandler()->maxSprite()); if (slot >= size()) - resize(slot + 1, NULL); + ensureSize(slot + 1); if (slot >= mSpriteIDs.size()) mSpriteIDs.resize(slot + 1, 0); @@ -1074,8 +1074,7 @@ void Being::setSprite(unsigned int slot, int id, const std::string &color, // id = 0 means unequip if (id == 0) { - delete at(slot); - at(slot) = NULL; + removeSprite(slot); if (isWeapon) mEquippedWeapon = NULL; @@ -1097,10 +1096,7 @@ void Being::setSprite(unsigned int slot, int id, const std::string &color, if (equipmentSprite) equipmentSprite->setDirection(getSpriteDirection()); - if (at(slot)) - delete at(slot); - - at(slot) = equipmentSprite; + CompoundSprite::setSprite(slot, equipmentSprite); if (isWeapon) mEquippedWeapon = &ItemDB::get(id); @@ -1124,7 +1120,7 @@ void Being::setSpriteColor(unsigned int slot, const std::string &color) int Being::getNumberOfLayers() const { - return size(); + return CompoundSprite::getNumberOfLayers(); } void Being::load() diff --git a/src/compoundsprite.cpp b/src/compoundsprite.cpp index a9a521ef..53fdd7c2 100644 --- a/src/compoundsprite.cpp +++ b/src/compoundsprite.cpp @@ -20,9 +20,24 @@ #include "compoundsprite.h" +#include "game.h" +#include "graphics.h" +#include "openglgraphics.h" +#include "map.h" + +#include "resources/image.h" + #include "utils/dtor.h" -CompoundSprite::CompoundSprite() +#include + +#define BUFFER_WIDTH 100 +#define BUFFER_HEIGHT 100 + +CompoundSprite::CompoundSprite(): + mImage(NULL), + mAlphaImage(NULL), + mNeedsRedraw(false) { mAlpha = 1.0f; } @@ -36,44 +51,73 @@ CompoundSprite::~CompoundSprite() clear(); } -void CompoundSprite::reset() +bool CompoundSprite::reset() { + bool ret = false; + SpriteIterator it, it_end; for (it = begin(), it_end = end(); it != it_end; it++) if (*it) - (*it)->reset(); + ret |= (*it)->reset(); + + mNeedsRedraw |= ret; + return ret; } -void CompoundSprite::play(SpriteAction action) +bool CompoundSprite::play(SpriteAction action) { + bool ret = false; + SpriteIterator it, it_end; for (it = begin(), it_end = end(); it != it_end; it++) if (*it) - (*it)->play(action); + ret |= (*it)->play(action); + + mNeedsRedraw |= ret; + return ret; } -void CompoundSprite::update(int time) +bool CompoundSprite::update(int time) { + bool ret = false; + SpriteIterator it, it_end; for (it = begin(), it_end = end(); it != it_end; it++) if (*it) - (*it)->update(time); + ret |= (*it)->update(time); + + mNeedsRedraw |= ret; + return ret; } bool CompoundSprite::draw(Graphics* graphics, int posX, int posY) const { - SpriteConstIterator it, it_end; - for (it = begin(), it_end = end(); it != it_end; it++) + if (mNeedsRedraw) + redraw(); + + if (mAlpha == 1.0f && mImage) + return graphics->drawImage(mImage, posX + mOffsetX, posY + mOffsetY); + else if (mAlpha && mAlphaImage) { - if (*it) + if (mAlphaImage->getAlpha() != mAlpha) + mAlphaImage->setAlpha(mAlpha); + + return graphics->drawImage(mAlphaImage, + posX + mOffsetX, posY + mOffsetY); + } + else + { + SpriteConstIterator it, it_end; + for (it = begin(), it_end = end(); it != it_end; it++) { - if ((*it)->getAlpha() != mAlpha) - (*it)->setAlpha(mAlpha); - (*it)->draw(graphics, posX, posY); + if (*it) + { + (*it)->draw(graphics, posX, posY); + } } } - return true; + return false; } int CompoundSprite::getWidth() const @@ -106,16 +150,155 @@ int CompoundSprite::getHeight() const return 0; } -Image* CompoundSprite::getImage() const +const Image* CompoundSprite::getImage() const { - // TODO http://bugs.manasource.org/view.php?id=24 - return NULL; + return mImage; } -void CompoundSprite::setDirection(SpriteDirection direction) +bool CompoundSprite::setDirection(SpriteDirection direction) { + bool ret = false; + SpriteIterator it, it_end; for (it = begin(), it_end = end(); it != it_end; it++) if (*it) - (*it)->setDirection(direction); + ret |= (*it)->setDirection(direction); + + mNeedsRedraw |= ret; + return ret; +} + +int CompoundSprite::getNumberOfLayers() const +{ + if (mImage || mAlphaImage) + { + return 1; + } + else + { + return size(); + } +} + +void CompoundSprite::addSprite(Sprite* sprite) +{ + push_back(sprite); + mNeedsRedraw = true; +} + +void CompoundSprite::setSprite(int layer, Sprite* sprite) +{ + // Skip if it won't change anything + if (at(layer) == sprite) + return; + + if (at(layer)) + delete at(layer); + at(layer) = sprite; + mNeedsRedraw = true; +} + +void CompoundSprite::removeSprite(int layer) +{ + // Skip if it won't change anything + if (at(layer) == NULL) + return; + + delete at(layer); + at(layer) = NULL; + mNeedsRedraw = true; +} + +void CompoundSprite::clear() +{ + // Skip if it won't change anything + if (empty()) + return; + + std::vector::clear(); + mNeedsRedraw = true; +} + +void CompoundSprite::ensureSize(size_t layerCount) +{ + // Skip if it won't change anything + if (size() >= layerCount) + return; + + resize(layerCount, NULL); + mNeedsRedraw = true; +} + +void CompoundSprite::redraw() const +{ + // TODO OpenGL support + if (Image::getLoadAsOpenGL()) + { + mNeedsRedraw = false; + return; + } + +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + int rmask = 0xff000000; + int gmask = 0x00ff0000; + int bmask = 0x0000ff00; + int amask = 0x000000ff; +#else + int rmask = 0x000000ff; + int gmask = 0x0000ff00; + int bmask = 0x00ff0000; + int amask = 0xff000000; +#endif + + SDL_Surface *surface = SDL_CreateRGBSurface(SDL_HWSURFACE, + BUFFER_WIDTH, BUFFER_HEIGHT, + 32, rmask, gmask, bmask, amask); + + if (!surface) + return; + + Graphics *graphics = new Graphics(); + graphics->setBlitMode(Graphics::BLIT_GFX); + graphics->setTarget(surface); + graphics->_beginDraw(); + + int tileX = 32 / 2; + int tileY = 32; + + Game *game = Game::instance(); + if (game) + { + Map *map = game->getCurrentMap(); + tileX = map->getTileWidth() / 2; + tileY = map->getTileWidth(); + } + + int posX = BUFFER_WIDTH / 2 - tileX; + int posY = BUFFER_HEIGHT - tileY; + + mOffsetX = tileX - BUFFER_WIDTH / 2; + mOffsetY = tileY - BUFFER_HEIGHT; + + SpriteConstIterator it, it_end; + for (it = begin(), it_end = end(); it != it_end; it++) + { + if (*it) + { + (*it)->draw(graphics, posX, posY); + } + } + + delete graphics; + + SDL_Surface *surfaceA = SDL_CreateRGBSurface(SDL_HWSURFACE, + BUFFER_WIDTH, BUFFER_HEIGHT, + 32, rmask, gmask, bmask, amask); + + SDL_SetAlpha(surface, 0, SDL_ALPHA_OPAQUE); + SDL_BlitSurface(surface, NULL, surfaceA, NULL); + + mImage = Image::load(surface); + mAlphaImage = Image::load(surfaceA); + + mNeedsRedraw = false; } diff --git a/src/compoundsprite.h b/src/compoundsprite.h index b3925216..c714c34b 100644 --- a/src/compoundsprite.h +++ b/src/compoundsprite.h @@ -25,18 +25,20 @@ #include -class CompoundSprite : public Sprite, public std::vector +class Image; + +class CompoundSprite : public Sprite, private std::vector { public: CompoundSprite(); ~CompoundSprite(); - virtual void reset(); + virtual bool reset(); - virtual void play(SpriteAction action); + virtual bool play(SpriteAction action); - virtual void update(int time); + virtual bool update(int time); virtual bool draw(Graphics* graphics, int posX, int posY) const; @@ -50,18 +52,40 @@ public: */ virtual int getHeight() const; - virtual Image* getImage() const; + virtual const Image* getImage() const; - virtual void setDirection(SpriteDirection direction); + virtual bool setDirection(SpriteDirection direction); - virtual Sprite *getSprite(int index) const - { return at(index); } + int getNumberOfLayers() const; - int getNumberOfLayers() - { return size(); } -}; + size_t size() const + { return std::vector::size(); } + + void addSprite(Sprite* sprite); + + void setSprite(int layer, Sprite* sprite); + + Sprite *getSprite(int layer) const + { return at(layer); } + + void removeSprite(int layer); + + void clear(); -typedef CompoundSprite::iterator SpriteIterator; -typedef CompoundSprite::const_iterator SpriteConstIterator; + void ensureSize(size_t layerCount); + +private: + typedef CompoundSprite::iterator SpriteIterator; + typedef CompoundSprite::const_iterator SpriteConstIterator; + + void redraw() const; + + mutable Image *mImage; + mutable Image *mAlphaImage; + + mutable int mOffsetX, mOffsetY; + + mutable bool mNeedsRedraw; +}; #endif // COMPOUNDSPRITE_H diff --git a/src/graphics.cpp b/src/graphics.cpp index 9815e1ad..24f92544 100644 --- a/src/graphics.cpp +++ b/src/graphics.cpp @@ -27,12 +27,15 @@ #include "resources/image.h" #include "resources/imageloader.h" +#include + Graphics::Graphics(): mWidth(0), mHeight(0), mBpp(0), mFullscreen(false), - mHWAccel(false) + mHWAccel(false), + mBlitMode(BLIT_NORMAL) { } @@ -183,7 +186,10 @@ bool Graphics::drawImage(Image *image, int srcX, int srcY, int dstX, int dstY, srcRect.w = width; srcRect.h = height; - return !(SDL_BlitSurface(image->mSDLSurface, &srcRect, mTarget, &dstRect) < 0); + if (mBlitMode == BLIT_NORMAL) + return !(SDL_BlitSurface(image->mSDLSurface, &srcRect, mTarget, &dstRect) < 0); + else + return !(SDL_gfxBlitRGBA(image->mSDLSurface, &srcRect, mTarget, &dstRect) < 0); } void Graphics::drawImage(gcn::Image const *image, int srcX, int srcY, diff --git a/src/graphics.h b/src/graphics.h index 211fb901..344c31c3 100644 --- a/src/graphics.h +++ b/src/graphics.h @@ -73,6 +73,11 @@ struct ImageRect class Graphics : public gcn::SDLGraphics { public: + enum BlitMode { + BLIT_NORMAL = 0, + BLIT_GFX + }; + /** * Constructor. */ @@ -182,6 +187,12 @@ class Graphics : public gcn::SDLGraphics drawImageRect(area.x, area.y, area.width, area.height, imgRect); } + void setBlitMode(BlitMode mode) + { mBlitMode = mode; } + + BlitMode getBlitMode() + { return mBlitMode; } + /** * Updates the screen. This is done by either copying the buffer to the * screen or swapping pages. @@ -211,6 +222,7 @@ class Graphics : public gcn::SDLGraphics int mBpp; bool mFullscreen; bool mHWAccel; + BlitMode mBlitMode; }; extern Graphics *graphics; diff --git a/src/imagesprite.h b/src/imagesprite.h index 83fcfa12..20f9fbef 100644 --- a/src/imagesprite.h +++ b/src/imagesprite.h @@ -34,11 +34,14 @@ public: ~ImageSprite(); - virtual void reset() {} + virtual bool reset() + { return false; } - virtual void play(SpriteAction action) {} + virtual bool play(SpriteAction action) + { return false; } - virtual void update(int time) {} + virtual bool update(int time) + { return false; } virtual bool draw(Graphics* graphics, int posX, int posY) const; @@ -48,10 +51,11 @@ public: virtual int getHeight() const { return mImage->getHeight(); } - virtual Image* getImage() const + virtual const Image* getImage() const { return mImage; } - virtual void setDirection(SpriteDirection direction) {} + virtual bool setDirection(SpriteDirection direction) + { return false; } int getNumberOfLayers() { return 1; } diff --git a/src/sprite.h b/src/sprite.h index d8345ab8..2bcd754a 100644 --- a/src/sprite.h +++ b/src/sprite.h @@ -33,19 +33,25 @@ class Sprite /** * Resets the sprite. + * + * @returns true if the sprite changed, false otherwise */ - virtual void reset() = 0; + virtual bool reset() = 0; /** - * Plays an action using the current direction + * Plays an action using the current direction. + * + * @returns true if the sprite changed, false otherwise */ - virtual void play(SpriteAction action) = 0; + virtual bool play(SpriteAction action) = 0; /** * Inform the animation of the passed time so that it can output the * correct animation frame. + * + * @returns true if the sprite changed, false otherwise */ - virtual void update(int time) = 0; + virtual bool update(int time) = 0; /** * Draw the current animation frame at the coordinates given in screen @@ -66,12 +72,14 @@ class Sprite /** * Returns a reference to the current image being drawn. */ - virtual Image* getImage() const = 0; + virtual const Image* getImage() const = 0; /** * Sets the direction. + * + * @returns true if the sprite changed, false otherwise */ - virtual void setDirection(SpriteDirection direction) = 0; + virtual bool setDirection(SpriteDirection direction) = 0; /** * Sets the alpha value of the animated sprite -- cgit v1.2.3-70-g09d2