summaryrefslogtreecommitdiff
path: root/src/being/compoundsprite.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/being/compoundsprite.cpp')
-rw-r--r--src/being/compoundsprite.cpp567
1 files changed, 567 insertions, 0 deletions
diff --git a/src/being/compoundsprite.cpp b/src/being/compoundsprite.cpp
new file mode 100644
index 000000000..68a0d42e3
--- /dev/null
+++ b/src/being/compoundsprite.cpp
@@ -0,0 +1,567 @@
+/*
+ * The ManaPlus Client
+ * Copyright (C) 2010 The Mana Developers
+ * Copyright (C) 2011-2013 The ManaPlus Developers
+ *
+ * This file is part of The ManaPlus Client.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "being/compoundsprite.h"
+
+#include "client.h"
+#include "configuration.h"
+#include "game.h"
+
+#ifdef USE_OPENGL
+#include "main.h"
+#endif
+
+#include "map.h"
+#include "sdlshared.h"
+
+#include "render/surfacegraphics.h"
+
+#include "resources/image.h"
+#include "resources/imagehelper.h"
+
+#include "utils/dtor.h"
+#include "utils/sdlcheckutils.h"
+
+#include <SDL.h>
+
+#include "debug.h"
+
+static const int BUFFER_WIDTH = 100;
+static const int BUFFER_HEIGHT = 100;
+
+static const unsigned cache_max_size = 10;
+static const unsigned cache_clean_part = 3;
+bool CompoundSprite::mEnableDelay = true;
+
+CompoundSprite::CompoundSprite() :
+ imagesCache(),
+ mCacheItem(nullptr),
+ mImage(nullptr),
+ mAlphaImage(nullptr),
+ mOffsetX(0),
+ mOffsetY(0),
+ mSprites(),
+ mNextRedrawTime(0),
+ mNeedsRedraw(false),
+ mEnableAlphaFix(config.getBoolValue("enableAlphaFix")),
+ mDisableAdvBeingCaching(config.getBoolValue("disableAdvBeingCaching")),
+ mDisableBeingCaching(config.getBoolValue("disableBeingCaching"))
+{
+ mAlpha = 1.0f;
+}
+
+CompoundSprite::~CompoundSprite()
+{
+ clear();
+ mImage = nullptr;
+ mAlphaImage = nullptr;
+}
+
+bool CompoundSprite::reset()
+{
+ bool ret = false;
+ FOR_EACH (SpriteIterator, it, mSprites)
+ {
+ if (*it)
+ ret |= (*it)->reset();
+ }
+ mNeedsRedraw |= ret;
+ return ret;
+}
+
+bool CompoundSprite::play(const std::string &action)
+{
+ bool ret = false;
+ FOR_EACH (SpriteIterator, it, mSprites)
+ {
+ if (*it)
+ ret |= (*it)->play(action);
+ }
+ mNeedsRedraw |= ret;
+ return ret;
+}
+
+bool CompoundSprite::update(const int time)
+{
+ bool ret = false;
+ FOR_EACH (SpriteIterator, it, mSprites)
+ {
+ if (*it)
+ ret |= (*it)->update(time);
+ }
+ mNeedsRedraw |= ret;
+ return ret;
+}
+
+bool CompoundSprite::draw(Graphics *const graphics,
+ const int posX, const int posY) const
+{
+ FUNC_BLOCK("CompoundSprite::draw", 1)
+ if (mNeedsRedraw)
+ updateImages();
+
+ if (mSprites.empty()) // Nothing to draw
+ return false;
+
+ if (mAlpha == 1.0f && mImage)
+ {
+ return graphics->drawImage(mImage, posX + mOffsetX, posY + mOffsetY);
+ }
+ else if (mAlpha && mAlphaImage)
+ {
+ mAlphaImage->setAlpha(mAlpha);
+ return graphics->drawImage(mAlphaImage,
+ posX + mOffsetX, posY + mOffsetY);
+ }
+ else
+ {
+ drawSprites(graphics, posX, posY);
+ }
+ return false;
+}
+
+void CompoundSprite::drawSprites(Graphics *const graphics,
+ const int posX, const int posY) const
+{
+ FOR_EACH (SpriteConstIterator, it, mSprites)
+ {
+ if (*it)
+ {
+ (*it)->setAlpha(mAlpha);
+ (*it)->draw(graphics, posX, posY);
+ }
+ }
+}
+
+void CompoundSprite::drawSpritesSDL(Graphics *const graphics,
+ const int posX, const int posY) const
+{
+ FOR_EACH (SpriteConstIterator, it, mSprites)
+ {
+ if (*it)
+ (*it)->draw(graphics, posX, posY);
+ }
+}
+
+int CompoundSprite::getWidth() const
+{
+ FOR_EACH (SpriteConstIterator, it, mSprites)
+ {
+ const Sprite *const base = *it;
+ if (base)
+ return base->getWidth();
+ }
+
+ return 0;
+}
+
+int CompoundSprite::getHeight() const
+{
+ FOR_EACH (SpriteConstIterator, it, mSprites)
+ {
+ const Sprite *const base = *it;
+ if (base)
+ return base->getHeight();
+ }
+
+ return 0;
+}
+
+const Image *CompoundSprite::getImage() const
+{
+ return mImage;
+}
+
+bool CompoundSprite::setSpriteDirection(const SpriteDirection direction)
+{
+ bool ret = false;
+ FOR_EACH (SpriteIterator, it, mSprites)
+ {
+ if (*it)
+ ret |= (*it)->setSpriteDirection(direction);
+ }
+ mNeedsRedraw |= ret;
+ return ret;
+}
+
+int CompoundSprite::getNumberOfLayers() const
+{
+ if (mImage || mAlphaImage)
+ return 1;
+ else
+ return static_cast<int>(size());
+}
+
+unsigned int CompoundSprite::getCurrentFrame() const
+{
+ FOR_EACH (SpriteConstIterator, it, mSprites)
+ {
+ if (*it)
+ return (*it)->getCurrentFrame();
+ }
+ return 0;
+}
+
+unsigned int CompoundSprite::getFrameCount() const
+{
+ FOR_EACH (SpriteConstIterator, it, mSprites)
+ {
+ if (*it)
+ return (*it)->getFrameCount();
+ }
+ return 0;
+}
+
+void CompoundSprite::addSprite(Sprite *const sprite)
+{
+ mSprites.push_back(sprite);
+ mNeedsRedraw = true;
+}
+
+void CompoundSprite::setSprite(const int layer, Sprite *const sprite)
+{
+ // Skip if it won't change anything
+ if (mSprites.at(layer) == sprite)
+ return;
+
+ delete mSprites.at(layer);
+ mSprites[layer] = sprite;
+ mNeedsRedraw = true;
+}
+
+void CompoundSprite::removeSprite(const int layer)
+{
+ // Skip if it won't change anything
+ if (!mSprites.at(layer))
+ return;
+
+ delete mSprites.at(layer);
+ mSprites.at(layer) = nullptr;
+ mNeedsRedraw = true;
+}
+
+void CompoundSprite::clear()
+{
+ // Skip if it won't change anything
+ if (!mSprites.empty())
+ {
+ delete_all(mSprites);
+ mSprites.clear();
+ }
+ mNeedsRedraw = true;
+ delete_all(imagesCache);
+ imagesCache.clear();
+ delete mCacheItem;
+ mCacheItem = nullptr;
+}
+
+void CompoundSprite::ensureSize(size_t layerCount)
+{
+ // Skip if it won't change anything
+ if (mSprites.size() >= layerCount)
+ return;
+
+// resize(layerCount, nullptr);
+ mSprites.resize(layerCount);
+}
+
+/**
+ * Returns the curent frame in the current animation of the given layer.
+ */
+unsigned int CompoundSprite::getCurrentFrame(unsigned int layer) const
+{
+ if (layer >= mSprites.size())
+ return 0;
+
+ const Sprite *const s = getSprite(layer);
+ if (s)
+ return s->getCurrentFrame();
+
+ return 0;
+}
+
+/**
+ * Returns the frame count in the current animation of the given layer.
+ */
+unsigned int CompoundSprite::getFrameCount(unsigned int layer)
+{
+ if (layer >= mSprites.size())
+ return 0;
+
+ const Sprite *const s = getSprite(layer);
+ if (s)
+ return s->getFrameCount();
+
+ return 0;
+}
+
+void CompoundSprite::redraw() const
+{
+#ifndef USE_SDL2
+
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+ const int rmask = 0xff000000;
+ const int gmask = 0x00ff0000;
+ const int bmask = 0x0000ff00;
+ const int amask = 0x000000ff;
+#else
+ const int rmask = 0x000000ff;
+ const int gmask = 0x0000ff00;
+ const int bmask = 0x00ff0000;
+ const int amask = 0xff000000;
+#endif
+
+ SDL_Surface *const surface = MSDL_CreateRGBSurface(SDL_HWSURFACE,
+ BUFFER_WIDTH, BUFFER_HEIGHT, 32, rmask, gmask, bmask, amask);
+
+ if (!surface)
+ return;
+
+ SurfaceGraphics *graphics = new SurfaceGraphics();
+ graphics->setBlitMode(SurfaceGraphics::BLIT_GFX);
+ graphics->setTarget(surface);
+ graphics->_beginDraw();
+
+ int tileX = 32 / 2;
+ int tileY = 32;
+
+ const Game *const game = Game::instance();
+ if (game)
+ {
+ const Map *const map = game->getCurrentMap();
+ if (map)
+ {
+ tileX = map->getTileWidth() / 2;
+ tileY = map->getTileWidth();
+ }
+ }
+
+ const int posX = BUFFER_WIDTH / 2 - tileX;
+ const int posY = BUFFER_HEIGHT - tileY;
+
+ mOffsetX = tileX - BUFFER_WIDTH / 2;
+ mOffsetY = tileY - BUFFER_HEIGHT;
+
+ drawSpritesSDL(graphics, posX, posY);
+
+ delete graphics;
+ graphics = nullptr;
+
+ SDL_Surface *const surfaceA = MSDL_CreateRGBSurface(SDL_HWSURFACE,
+ BUFFER_WIDTH, BUFFER_HEIGHT, 32, rmask, gmask, bmask, amask);
+
+#ifdef USE_SDL2
+ SDL_SetSurfaceAlphaMod(surface, 255);
+#else
+ SDL_SetAlpha(surface, 0, SDL_ALPHA_OPAQUE);
+#endif
+ SDL_BlitSurface(surface, nullptr, surfaceA, nullptr);
+
+ delete mImage;
+ delete mAlphaImage;
+
+ mImage = imageHelper->load(surface);
+ MSDL_FreeSurface(surface);
+
+ if (ImageHelper::mEnableAlpha)
+ {
+ mAlphaImage = imageHelper->load(surfaceA);
+ MSDL_FreeSurface(surfaceA);
+ }
+ else
+ {
+ mAlphaImage = nullptr;
+ }
+#endif
+}
+
+void CompoundSprite::setAlpha(float alpha)
+{
+ if (alpha != mAlpha)
+ {
+#ifdef USE_OPENGL
+ if (mEnableAlphaFix && imageHelper->useOpenGL() == 0
+ && size() > 3)
+#else
+ if (mEnableAlphaFix && size() > 3)
+#endif
+ {
+ FOR_EACH (SpriteConstIterator, it, mSprites)
+ {
+ if (*it)
+ (*it)->setAlpha(alpha);
+ }
+ }
+ mAlpha = alpha;
+ }
+}
+
+void CompoundSprite::updateImages() const
+{
+#ifndef USE_SDL2
+#ifdef USE_OPENGL
+ if (imageHelper->useOpenGL())
+ return;
+#endif
+
+ if (mEnableDelay)
+ {
+ if (get_elapsed_time1(mNextRedrawTime) < 10)
+ return;
+ mNextRedrawTime = tick_time;
+ }
+ mNeedsRedraw = false;
+
+ if (!mDisableBeingCaching)
+ {
+ if (size() <= 3)
+ return;
+
+ if (!mDisableAdvBeingCaching)
+ {
+ if (updateFromCache())
+ return;
+
+ redraw();
+
+ if (mImage)
+ initCurrentCacheItem();
+ }
+ else
+ {
+ redraw();
+ }
+ }
+#endif
+}
+
+bool CompoundSprite::updateFromCache() const
+{
+#ifndef USE_SDL2
+// static int hits = 0;
+// static int miss = 0;
+
+ if (mCacheItem && mCacheItem->image)
+ {
+ imagesCache.push_front(mCacheItem);
+ mCacheItem = nullptr;
+ if (imagesCache.size() > cache_max_size)
+ {
+ for (unsigned f = 0; f < cache_clean_part; f ++)
+ {
+ CompoundItem *item = imagesCache.back();
+ imagesCache.pop_back();
+ delete item;
+ }
+ }
+ }
+
+// logger->log("cache size: %d, hit %d, miss %d",
+// (int)imagesCache.size(), hits, miss);
+
+ const size_t sz = size();
+ FOR_EACH (ImagesCache::iterator, it, imagesCache)
+ {
+ CompoundItem *const ic = *it;
+ if (ic && ic->data.size() == sz)
+ {
+ bool fail(false);
+ VectorPointers::const_iterator it2 = ic->data.begin();
+ const VectorPointers::const_iterator it2_end = ic->data.end();
+
+ for (SpriteConstIterator it1 = mSprites.begin(),
+ it1_end = mSprites.end();
+ it1 != it1_end && it2 != it2_end;
+ ++ it1, ++ it2)
+ {
+ const void *ptr1 = nullptr;
+ const void *ptr2 = nullptr;
+ if (*it1)
+ ptr1 = (*it1)->getHash();
+ if (*it2)
+ ptr2 = *it2;
+ if (ptr1 != ptr2)
+ {
+ fail = true;
+ break;
+ }
+ }
+ if (!fail)
+ {
+// hits ++;
+ mImage = (*it)->image;
+ mAlphaImage = (*it)->alphaImage;
+ imagesCache.erase(it);
+ mCacheItem = ic;
+ return true;
+ }
+ }
+ }
+ mImage = nullptr;
+ mAlphaImage = nullptr;
+// miss++;
+#endif
+ return false;
+}
+
+void CompoundSprite::initCurrentCacheItem() const
+{
+ delete mCacheItem;
+ mCacheItem = new CompoundItem();
+ mCacheItem->image = mImage;
+ mCacheItem->alphaImage = mAlphaImage;
+// mCacheItem->alpha = mAlpha;
+
+ FOR_EACH (SpriteConstIterator, it, mSprites)
+ {
+ if (*it)
+ mCacheItem->data.push_back((*it)->getHash());
+ else
+ mCacheItem->data.push_back(nullptr);
+ }
+}
+
+bool CompoundSprite::updateNumber(unsigned num)
+{
+ bool res(false);
+ FOR_EACH (SpriteConstIterator, it, mSprites)
+ {
+ if (*it)
+ {
+ if ((*it)->updateNumber(num))
+ res = true;
+ }
+ }
+ return res;
+}
+
+CompoundItem::CompoundItem() :
+ data(),
+ image(nullptr),
+ alphaImage(nullptr)
+{
+}
+
+CompoundItem::~CompoundItem()
+{
+ delete image;
+ delete alphaImage;
+}