diff options
author | Thorbjørn Lindeijer <bjorn@lindeijer.nl> | 2025-02-24 11:41:53 +0100 |
---|---|---|
committer | Thorbjørn Lindeijer <bjorn@lindeijer.nl> | 2025-02-26 21:18:09 +0000 |
commit | 593361ba81764dae93c02bd5bc4ee238db55aac1 (patch) | |
tree | 90d295bb82a1534fd750c27401f29652ecb95061 /src | |
parent | a5d1cc6d7e37c4ca45b2dc390074e01f9a9589b7 (diff) | |
download | mana-593361ba81764dae93c02bd5bc4ee238db55aac1.tar.gz mana-593361ba81764dae93c02bd5bc4ee238db55aac1.tar.bz2 mana-593361ba81764dae93c02bd5bc4ee238db55aac1.tar.xz mana-593361ba81764dae93c02bd5bc4ee238db55aac1.zip |
Use ResourceRef for all resource types
All ResourceManager functions that load resources now return respective
ResourceRef values, which helps to make sure resources are properly
cleaned up.
The Sound class was cleaned up and now also allows SoundEffect resources
to be unloaded.
The Animation class now keeps its ImageSet loaded only as long as
necessary. Previously, SimpleAnimation and ParticleEmitter would keep
the ImageSet loaded indefinitely by never decreasing its reference
count.
Reduced duplicated animation loading code between SimpleAnimation and
ParticleEmitter.
Diffstat (limited to 'src')
69 files changed, 450 insertions, 726 deletions
diff --git a/src/actorsprite.cpp b/src/actorsprite.cpp index f22fbd2d..28d5bed3 100644 --- a/src/actorsprite.cpp +++ b/src/actorsprite.cpp @@ -40,7 +40,7 @@ #define EFFECTS_FILE "effects.xml" -ImageSet *ActorSprite::targetCursorImages[2][NUM_TC]; +ResourceRef<ImageSet> ActorSprite::targetCursorImages[2][NUM_TC]; SimpleAnimation *ActorSprite::targetCursor[2][NUM_TC]; bool ActorSprite::loaded = false; @@ -212,8 +212,7 @@ void ActorSprite::cleanupTargetCursors() for (int type = 0; type < NUM_TCT; type++) { delete targetCursor[type][size]; - if (targetCursorImages[type][size]) - targetCursorImages[type][size]->decRef(); + targetCursorImages[type][size] = nullptr; } } } @@ -225,8 +224,7 @@ void ActorSprite::loadTargetCursor(const std::string &filename, assert(size < 3); ResourceManager *resman = ResourceManager::getInstance(); - ImageSet *currentImageSet = resman->getImageSet(filename, width, height); - + auto currentImageSet = resman->getImageSet(filename, width, height); if (!currentImageSet) { logger->log("Error loading target cursor: %s", filename.c_str()); diff --git a/src/actorsprite.h b/src/actorsprite.h index 93a4551f..5993e4ea 100644 --- a/src/actorsprite.h +++ b/src/actorsprite.h @@ -26,7 +26,6 @@ #include "particle.h" class SimpleAnimation; -class StatusEffect; class ActorSprite : public Actor { @@ -140,7 +139,7 @@ private: int width, int height, int type, int size); /** Images of the target cursor. */ - static ImageSet *targetCursorImages[NUM_TCT][NUM_TC]; + static ResourceRef<ImageSet> targetCursorImages[NUM_TCT][NUM_TC]; /** Animated target cursors. */ static SimpleAnimation *targetCursor[NUM_TCT][NUM_TC]; diff --git a/src/client.cpp b/src/client.cpp index 5c114911..e5178048 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -410,6 +410,8 @@ Client::~Client() CharDB::unload(); delete itemDb; + ActorSprite::unload(); + // Before config.write() since it writes the shortcuts to the config delete itemShortcut; delete emoteShortcut; diff --git a/src/flooritem.cpp b/src/flooritem.cpp index 330936ec..9c545560 100644 --- a/src/flooritem.cpp +++ b/src/flooritem.cpp @@ -54,12 +54,9 @@ FloorItem::FloorItem(int id, ResourceManager *resman = ResourceManager::getInstance(); std::string imagePath = paths.getStringValue("itemIcons") + info.display.image; - mImage = resman->getImageRef(imagePath); + mImage = resman->getImage(imagePath); if (!mImage) - { - imagePath = Theme::resolveThemePath(paths.getStringValue("unknownItemFile")); - mImage = resman->getImageRef(imagePath); - } + mImage = Theme::getImageFromTheme(paths.getStringValue("unknownItemFile")); } } diff --git a/src/gui/emotepopup.cpp b/src/gui/emotepopup.cpp index a906eb7e..e759ab25 100644 --- a/src/gui/emotepopup.cpp +++ b/src/gui/emotepopup.cpp @@ -52,10 +52,7 @@ EmotePopup::EmotePopup() setVisible(true); } -EmotePopup::~EmotePopup() -{ - mSelectionImage->decRef(); -} +EmotePopup::~EmotePopup() = default; void EmotePopup::draw(gcn::Graphics *graphics) { diff --git a/src/gui/emotepopup.h b/src/gui/emotepopup.h index 19336426..c95c5723 100644 --- a/src/gui/emotepopup.h +++ b/src/gui/emotepopup.h @@ -23,6 +23,7 @@ #pragma once #include "gui/widgets/popup.h" +#include "resources/resource.h" #include <guichan/mouselistener.hpp> @@ -103,7 +104,7 @@ class EmotePopup : public Popup */ void distributeValueChangedEvent(); - Image *mSelectionImage; + ResourceRef<Image> mSelectionImage; int mSelectedEmoteId = -1; int mHoveredEmoteIndex = -1; diff --git a/src/gui/equipmentwindow.cpp b/src/gui/equipmentwindow.cpp index e7eeb048..569bcf8b 100644 --- a/src/gui/equipmentwindow.cpp +++ b/src/gui/equipmentwindow.cpp @@ -96,10 +96,7 @@ void EquipmentWindow::loadEquipBoxes() Net::getInventoryHandler()->getBoxBackground(i); if (!backgroundFile.empty()) - { - box.backgroundImage = - Theme::instance()->getImageFromTheme(backgroundFile); - } + box.backgroundImage = Theme::getImageFromTheme(backgroundFile); } } diff --git a/src/gui/equipmentwindow.h b/src/gui/equipmentwindow.h index a9635279..a5cda4e3 100644 --- a/src/gui/equipmentwindow.h +++ b/src/gui/equipmentwindow.h @@ -28,6 +28,8 @@ #include <guichan/actionlistener.hpp> +#include <vector> + class Inventory; class Item; class ItemPopup; @@ -72,7 +74,7 @@ class EquipmentWindow : public Window, public gcn::ActionListener { int posX = 0; int posY = 0; - Image *backgroundImage = nullptr; + ResourceRef<Image> backgroundImage; }; std::vector<EquipBox> mBoxes; /**< Equipment boxes. */ diff --git a/src/gui/itempopup.cpp b/src/gui/itempopup.cpp index 7f1dec3b..c21f4003 100644 --- a/src/gui/itempopup.cpp +++ b/src/gui/itempopup.cpp @@ -153,8 +153,8 @@ void ItemPopup::setItem(const ItemInfo &item, bool showImage) if (showImage) { ResourceManager *resman = ResourceManager::getInstance(); - auto image = resman->getImageRef(paths.getStringValue("itemIcons") + - item.display.image); + auto image = resman->getImage(paths.getStringValue("itemIcons") + + item.display.image); mIcon->setImage(image); if (image) diff --git a/src/gui/minimap.cpp b/src/gui/minimap.cpp index 41044431..14e2a257 100644 --- a/src/gui/minimap.cpp +++ b/src/gui/minimap.cpp @@ -91,7 +91,7 @@ void Minimap::setMap(Map *map) minimapName = tempname; if (!minimapName.empty()) - mMapImage = resman->getImageRef(minimapName); + mMapImage = resman->getImage(minimapName); } if (mMapImage) diff --git a/src/gui/skilldialog.cpp b/src/gui/skilldialog.cpp index 5b85252d..b830064a 100644 --- a/src/gui/skilldialog.cpp +++ b/src/gui/skilldialog.cpp @@ -61,7 +61,7 @@ struct SkillInfo { unsigned short id; std::string name; - Image *icon = nullptr; + ResourceRef<Image> icon; bool modifiable; bool visible; SkillModel *model = nullptr; @@ -73,25 +73,16 @@ struct SkillInfo float progress; gcn::Color color; - ~SkillInfo() - { - if (icon) - icon->decRef(); - } + ~SkillInfo() = default; void setIcon(const std::string &iconPath) { ResourceManager *res = ResourceManager::getInstance(); if (!iconPath.empty()) - { icon = res->getImage(iconPath); - } if (!icon) - { - icon = Theme::getImageFromTheme( - paths.getStringValue("unknownItemFile")); - } + icon = Theme::getImageFromTheme(paths.getStringValue("unknownItemFile")); } void update(); diff --git a/src/gui/widgets/avatarlistbox.cpp b/src/gui/widgets/avatarlistbox.cpp index ec3327b2..47e29ed0 100644 --- a/src/gui/widgets/avatarlistbox.cpp +++ b/src/gui/widgets/avatarlistbox.cpp @@ -33,8 +33,8 @@ #include <guichan/font.hpp> int AvatarListBox::instances = 0; -Image *AvatarListBox::onlineIcon = nullptr; -Image *AvatarListBox::offlineIcon = nullptr; +ResourceRef<Image> AvatarListBox::onlineIcon; +ResourceRef<Image> AvatarListBox::offlineIcon; AvatarListBox::AvatarListBox(AvatarListModel *model): ListBox(model) @@ -56,8 +56,8 @@ AvatarListBox::~AvatarListBox() if (instances == 0) { - onlineIcon->decRef(); - offlineIcon->decRef(); + onlineIcon = nullptr; + offlineIcon = nullptr; } } diff --git a/src/gui/widgets/avatarlistbox.h b/src/gui/widgets/avatarlistbox.h index 638e6223..9b0588ac 100644 --- a/src/gui/widgets/avatarlistbox.h +++ b/src/gui/widgets/avatarlistbox.h @@ -23,6 +23,7 @@ #include "avatar.h" #include "gui/widgets/listbox.h" +#include "resources/resource.h" #include <string> @@ -53,6 +54,6 @@ public: private: static int instances; - static Image *onlineIcon; - static Image *offlineIcon; + static ResourceRef<Image> onlineIcon; + static ResourceRef<Image> offlineIcon; }; diff --git a/src/gui/widgets/button.cpp b/src/gui/widgets/button.cpp index 274f329b..ce7a856f 100644 --- a/src/gui/widgets/button.cpp +++ b/src/gui/widgets/button.cpp @@ -39,7 +39,7 @@ float Button::mAlpha = 1.0; ImageRect *Button::mButton; TextPopup *Button::mTextPopup = nullptr; -enum{ +enum { BUTTON_STANDARD, // 0 BUTTON_HIGHLIGHTED, // 1 BUTTON_PRESSED, // 2 @@ -80,18 +80,17 @@ Button::Button(const std::string &caption, const std::string &actionEventId, adjustSize(); } -bool Button::setButtonIcon(const std::string& iconFile) +bool Button::setButtonIcon(const std::string &iconFile) { // We clean up possible older references. - if (mButtonIcon) - removeButtonIcon(); + removeButtonIcon(); // If nothing relevant was set, we can quit now. if (iconFile.empty()) return false; // Load the icon frames. - Image *btnIcons = Theme::getImageFromTheme(iconFile); + auto btnIcons = Theme::getImageFromTheme(iconFile); if (!btnIcons) return false; @@ -101,36 +100,27 @@ bool Button::setButtonIcon(const std::string& iconFile) if (frameWidth > 0 && frameHeight > 0) { - mButtonIcon = new Image*[BUTTON_COUNT]; + mButtonIcon.resize(BUTTON_COUNT); + for (int mode = 0; mode < BUTTON_COUNT; ++mode) { - mButtonIcon[mode] = btnIcons->getSubImage(mode * frameWidth, 0, - frameWidth, frameHeight); + mButtonIcon[mode].reset( + btnIcons->getSubImage(mode * frameWidth, 0, frameWidth, frameHeight)); } adjustSize(); } - btnIcons->decRef(); - return (mButtonIcon); + return !mButtonIcon.empty(); } -void Button::removeButtonIcon(bool adjustButtonSize) +void Button::removeButtonIcon() { - if (!mButtonIcon) + if (mButtonIcon.empty()) return; - // Delete potential button icons - for (int mode = 0; mode < BUTTON_COUNT; ++mode) - { - delete mButtonIcon[mode]; - mButtonIcon[mode] = nullptr; - } - delete[] mButtonIcon; - mButtonIcon = nullptr; - - if (adjustButtonSize) - adjustSize(); + mButtonIcon.clear(); + adjustSize(); } void Button::init() @@ -144,7 +134,7 @@ void Button::init() for (int mode = 0; mode < BUTTON_COUNT; ++mode) { - Image *modeImage = Theme::getImageFromTheme(data[mode].file); + auto modeImage = Theme::getImageFromTheme(data[mode].file); int a = 0; for (int y = 0; y < 3; y++) { @@ -157,7 +147,6 @@ void Button::init() a++; } } - modeImage->decRef(); } updateAlpha(); @@ -185,8 +174,6 @@ Button::~Button() delete mTextPopup; mTextPopup = nullptr; } - // Don' try to readjust the size when it's about to be deleted. - removeButtonIcon(false); } void Button::updateAlpha() @@ -227,15 +214,12 @@ void Button::draw(gcn::Graphics *graphics) else graphics->setColor(Theme::getThemeColor(Theme::BUTTON)); + Image *icon = mButtonIcon.empty() ? nullptr : mButtonIcon[mode].get(); int textX = 0; int textY = getHeight() / 2 - getFont()->getHeight() / 2; int btnIconX = 0; - int btnIconY = getHeight() / 2 - - ((mButtonIcon && mButtonIcon[mode]) ? - mButtonIcon[mode]->getHeight() / 2 : 0); - - int btnIconWidth = (mButtonIcon && mButtonIcon[mode]) ? - mButtonIcon[mode]->getWidth() : 0; + int btnIconY = getHeight() / 2 - (icon ? icon->getHeight() / 2 : 0); + int btnIconWidth = icon ? icon->getWidth() : 0; switch (getAlignment()) { @@ -243,7 +227,7 @@ void Button::draw(gcn::Graphics *graphics) if (btnIconWidth) { btnIconX = 4; - textX = btnIconX + mButtonIcon[mode]->getWidth() + 2; + textX = btnIconX + icon->getWidth() + 2; } else { @@ -254,8 +238,8 @@ void Button::draw(gcn::Graphics *graphics) if (btnIconWidth) { btnIconX = getWidth() / 2 - (getFont()->getWidth(mCaption) - + mButtonIcon[mode]->getWidth() + 2) / 2; - textX = getWidth() / 2 + mButtonIcon[mode]->getWidth() / 2 + 2; + + icon->getWidth() + 2) / 2; + textX = getWidth() / 2 + icon->getWidth() / 2 + 2; } else { @@ -280,8 +264,7 @@ void Button::draw(gcn::Graphics *graphics) } if (btnIconWidth) - static_cast<Graphics*>(graphics)->drawImage(mButtonIcon[mode], - btnIconX, btnIconY); + static_cast<Graphics *>(graphics)->drawImage(icon, btnIconX, btnIconY); graphics->drawText(getCaption(), textX, textY, getAlignment()); } @@ -289,14 +272,13 @@ void Button::adjustSize() { // Size of the image button. int iconWidth = 0, iconHeight = 0; - if (mButtonIcon) + if (!mButtonIcon.empty()) { for (int mode = 0; mode < BUTTON_COUNT; ++mode) { - iconWidth = std::max(iconWidth, mButtonIcon[mode] ? - mButtonIcon[mode]->getWidth() + 2 : 0); - iconHeight = std::max(iconHeight, mButtonIcon[mode] ? - mButtonIcon[mode]->getHeight() : 0); + const Image *icon = mButtonIcon[mode].get(); + iconWidth = std::max(iconWidth, icon->getWidth() + 2); + iconHeight = std::max(iconHeight, icon->getHeight()); } } diff --git a/src/gui/widgets/button.h b/src/gui/widgets/button.h index 0f63c9f4..1502dc43 100644 --- a/src/gui/widgets/button.h +++ b/src/gui/widgets/button.h @@ -23,6 +23,9 @@ #include <guichan/widgets/button.hpp> +#include <memory> +#include <vector> + class ImageRect; class Image; class TextPopup; @@ -88,13 +91,13 @@ class Button : public gcn::Button private: void init(); - void removeButtonIcon(bool adjustButtonSize = true); + void removeButtonIcon(); static ImageRect* mButton; /**< Button state graphics */ static int mInstances; /**< Number of button instances */ static float mAlpha; - Image** mButtonIcon = nullptr; /**< Button Icons graphics */ + std::vector<std::unique_ptr<Image>> mButtonIcon; /**< Button Icons graphics */ /** * The buttons popup diff --git a/src/gui/widgets/checkbox.cpp b/src/gui/widgets/checkbox.cpp index 274855fd..399ffeb4 100644 --- a/src/gui/widgets/checkbox.cpp +++ b/src/gui/widgets/checkbox.cpp @@ -41,7 +41,7 @@ CheckBox::CheckBox(const std::string &caption, bool selected): { if (instances == 0) { - Image *checkBox = Theme::getImageFromTheme("checkbox.png"); + auto checkBox = Theme::getImageFromTheme("checkbox.png"); checkBoxNormal = checkBox->getSubImage(0, 0, 9, 10); checkBoxChecked = checkBox->getSubImage(9, 0, 9, 10); checkBoxDisabled = checkBox->getSubImage(18, 0, 9, 10); @@ -54,7 +54,6 @@ CheckBox::CheckBox(const std::string &caption, bool selected): checkBoxDisabledChecked->setAlpha(mAlpha); checkBoxNormalHi->setAlpha(mAlpha); checkBoxCheckedHi->setAlpha(mAlpha); - checkBox->decRef(); } instances++; diff --git a/src/gui/widgets/desktop.cpp b/src/gui/widgets/desktop.cpp index c8ded9f5..e424beec 100644 --- a/src/gui/widgets/desktop.cpp +++ b/src/gui/widgets/desktop.cpp @@ -105,7 +105,7 @@ void Desktop::setBestFittingWallpaper() return; ResourceManager *resman = ResourceManager::getInstance(); - auto wallpaper = resman->getImageRef(wallpaperName); + auto wallpaper = resman->getImage(wallpaperName); if (wallpaper) { diff --git a/src/gui/widgets/dropdown.cpp b/src/gui/widgets/dropdown.cpp index 8811eb8d..2c78a3d7 100644 --- a/src/gui/widgets/dropdown.cpp +++ b/src/gui/widgets/dropdown.cpp @@ -37,7 +37,7 @@ #include <algorithm> int DropDown::instances = 0; -Image *DropDown::buttons[2][2]; +ResourceRef<Image> DropDown::buttons[2][2]; ImageRect DropDown::skin; float DropDown::mAlpha = 1.0; @@ -65,7 +65,7 @@ DropDown::DropDown(gcn::ListModel *listModel): buttons[1][1]->setAlpha(mAlpha); // get the border skin - Image *boxBorder = Theme::getImageFromTheme("deepbox.png"); + auto boxBorder = Theme::getImageFromTheme("deepbox.png"); int gridx[4] = {0, 3, 28, 31}; int gridy[4] = {0, 3, 28, 31}; int a = 0; @@ -84,8 +84,6 @@ DropDown::DropDown(gcn::ListModel *listModel): } skin.setAlpha(mAlpha); - - boxBorder->decRef(); } instances++; @@ -97,10 +95,10 @@ DropDown::~DropDown() // Free images memory if (instances == 0) { - buttons[0][0]->decRef(); - buttons[0][1]->decRef(); - buttons[1][0]->decRef(); - buttons[1][1]->decRef(); + buttons[0][0] = nullptr; + buttons[0][1] = nullptr; + buttons[1][0] = nullptr; + buttons[1][1] = nullptr; std::for_each(skin.grid, skin.grid + 9, dtor<Image*>()); } diff --git a/src/gui/widgets/dropdown.h b/src/gui/widgets/dropdown.h index 9022e196..7c4e6d1d 100644 --- a/src/gui/widgets/dropdown.h +++ b/src/gui/widgets/dropdown.h @@ -21,6 +21,7 @@ #pragma once +#include "resources/resource.h" #include <guichan/widgets/dropdown.hpp> class Image; @@ -81,7 +82,7 @@ class DropDown : public gcn::DropDown // Add own Images. static int instances; - static Image *buttons[2][2]; + static ResourceRef<Image> buttons[2][2]; static ImageRect skin; static float mAlpha; }; diff --git a/src/gui/widgets/emoteshortcutcontainer.cpp b/src/gui/widgets/emoteshortcutcontainer.cpp index 812377e9..7f33538f 100644 --- a/src/gui/widgets/emoteshortcutcontainer.cpp +++ b/src/gui/widgets/emoteshortcutcontainer.cpp @@ -40,26 +40,18 @@ EmoteShortcutContainer::EmoteShortcutContainer() mBackgroundImg = Theme::getImageFromTheme("item_shortcut_bgr.png"); - mBackgroundImg->setAlpha(config.guiAlpha); - mMaxItems = std::min(EmoteDB::getEmoteCount(), MAX_ITEMS); - mBoxHeight = mBackgroundImg->getHeight(); - mBoxWidth = mBackgroundImg->getWidth(); -} - -EmoteShortcutContainer::~EmoteShortcutContainer() -{ - mBackgroundImg->decRef(); + if (mBackgroundImg) + { + mBoxHeight = mBackgroundImg->getHeight(); + mBoxWidth = mBackgroundImg->getWidth(); + } } void EmoteShortcutContainer::draw(gcn::Graphics *graphics) { - if (config.guiAlpha != mAlpha) - { - mAlpha = config.guiAlpha; - mBackgroundImg->setAlpha(mAlpha); - } + mBackgroundImg->setAlpha(config.guiAlpha); auto *g = static_cast<Graphics*>(graphics); diff --git a/src/gui/widgets/emoteshortcutcontainer.h b/src/gui/widgets/emoteshortcutcontainer.h index 08133a50..57d5efd2 100644 --- a/src/gui/widgets/emoteshortcutcontainer.h +++ b/src/gui/widgets/emoteshortcutcontainer.h @@ -33,8 +33,6 @@ class EmoteShortcutContainer : public ShortcutContainer public: EmoteShortcutContainer(); - ~EmoteShortcutContainer() override; - /** * Draws the items. */ diff --git a/src/gui/widgets/icon.cpp b/src/gui/widgets/icon.cpp index 67fd8384..61506a6b 100644 --- a/src/gui/widgets/icon.cpp +++ b/src/gui/widgets/icon.cpp @@ -27,7 +27,7 @@ #include "resources/resourcemanager.h" Icon::Icon(const std::string &file) - : Icon(ResourceManager::getInstance()->getImageRef(file)) + : Icon(ResourceManager::getInstance()->getImage(file)) { } diff --git a/src/gui/widgets/itemcontainer.cpp b/src/gui/widgets/itemcontainer.cpp index 940c69f4..d1d00677 100644 --- a/src/gui/widgets/itemcontainer.cpp +++ b/src/gui/widgets/itemcontainer.cpp @@ -64,7 +64,6 @@ ItemContainer::ItemContainer(Inventory *inventory): ItemContainer::~ItemContainer() { - mSelImg->decRef(); delete mItemPopup; } diff --git a/src/gui/widgets/itemcontainer.h b/src/gui/widgets/itemcontainer.h index 2fb6bb9e..7c972347 100644 --- a/src/gui/widgets/itemcontainer.h +++ b/src/gui/widgets/itemcontainer.h @@ -21,6 +21,8 @@ #pragma once +#include "resources/resource.h" + #include <guichan/keylistener.hpp> #include <guichan/mouselistener.hpp> #include <guichan/widget.hpp> @@ -177,7 +179,7 @@ class ItemContainer : public gcn::Widget, Inventory *mInventory; int mGridColumns = 1; int mGridRows = 1; - Image *mSelImg; + ResourceRef<Image> mSelImg; int mSelectedIndex = -1; int mHighlightedIndex = -1; int mLastUsedSlot = -1; diff --git a/src/gui/widgets/itemshortcutcontainer.cpp b/src/gui/widgets/itemshortcutcontainer.cpp index 0b8f0c8c..594ad1ce 100644 --- a/src/gui/widgets/itemshortcutcontainer.cpp +++ b/src/gui/widgets/itemshortcutcontainer.cpp @@ -48,25 +48,21 @@ ItemShortcutContainer::ItemShortcutContainer() mBackgroundImg = Theme::getImageFromTheme("item_shortcut_bgr.png"); mMaxItems = itemShortcut->getItemCount(); - mBackgroundImg->setAlpha(config.guiAlpha); - - mBoxHeight = mBackgroundImg->getHeight(); - mBoxWidth = mBackgroundImg->getWidth(); + if (mBackgroundImg) + { + mBoxHeight = mBackgroundImg->getHeight(); + mBoxWidth = mBackgroundImg->getWidth(); + } } ItemShortcutContainer::~ItemShortcutContainer() { - mBackgroundImg->decRef(); delete mItemPopup; } void ItemShortcutContainer::draw(gcn::Graphics *graphics) { - if (config.guiAlpha != mAlpha) - { - mAlpha = config.guiAlpha; - mBackgroundImg->setAlpha(mAlpha); - } + mBackgroundImg->setAlpha(config.guiAlpha); auto *g = static_cast<Graphics*>(graphics); diff --git a/src/gui/widgets/playerbox.cpp b/src/gui/widgets/playerbox.cpp index 3bdd6bd1..9858727d 100644 --- a/src/gui/widgets/playerbox.cpp +++ b/src/gui/widgets/playerbox.cpp @@ -42,7 +42,7 @@ PlayerBox::PlayerBox(const Being *being): if (instances == 0) { // Load the background skin - Image *textbox = Theme::getImageFromTheme("deepbox.png"); + auto textbox = Theme::getImageFromTheme("deepbox.png"); int bggridx[4] = {0, 3, 28, 31}; int bggridy[4] = {0, 3, 28, 31}; int a = 0; @@ -60,8 +60,6 @@ PlayerBox::PlayerBox(const Being *being): } background.setAlpha(config.guiAlpha); - - textbox->decRef(); } instances++; diff --git a/src/gui/widgets/progressbar.cpp b/src/gui/widgets/progressbar.cpp index 9d41d1af..ec9690ab 100644 --- a/src/gui/widgets/progressbar.cpp +++ b/src/gui/widgets/progressbar.cpp @@ -56,7 +56,7 @@ ProgressBar::ProgressBar(float progress, if (mInstances == 0) { - Image *dBorders = Theme::getImageFromTheme("vscroll_grey.png"); + auto dBorders = Theme::getImageFromTheme("vscroll_grey.png"); mBorder.grid[0] = dBorders->getSubImage(0, 0, 4, 4); mBorder.grid[1] = dBorders->getSubImage(4, 0, 3, 4); mBorder.grid[2] = dBorders->getSubImage(7, 0, 4, 4); @@ -68,8 +68,6 @@ ProgressBar::ProgressBar(float progress, mBorder.grid[8] = dBorders->getSubImage(7, 15, 4, 4); mBorder.setAlpha(mAlpha); - - dBorders->decRef(); } mInstances++; diff --git a/src/gui/widgets/progressindicator.cpp b/src/gui/widgets/progressindicator.cpp index 496bd8a1..a103c8b4 100644 --- a/src/gui/widgets/progressindicator.cpp +++ b/src/gui/widgets/progressindicator.cpp @@ -24,7 +24,6 @@ #include "simpleanimation.h" #include "resources/animation.h" -#include "resources/imageset.h" #include "resources/resourcemanager.h" #include "resources/theme.h" @@ -32,12 +31,12 @@ ProgressIndicator::ProgressIndicator() { - ImageSet *images = Theme::getImageSetFromTheme("progress-indicator.png", - 32, 32); + const std::string path = Theme::resolveThemePath("progress-indicator.png"); + mImageSet = ResourceManager::getInstance()->getImageSet(path, 32, 32); Animation anim; - for (size_t i = 0; i < images->size(); ++i) - anim.addFrame(images->get(i), 100, 0, 0); + for (size_t i = 0; i < mImageSet->size(); ++i) + anim.addFrame(mImageSet->get(i), 100, 0, 0); mIndicator = std::make_unique<SimpleAnimation>(std::move(anim)); diff --git a/src/gui/widgets/progressindicator.h b/src/gui/widgets/progressindicator.h index cb66a887..4a6ea339 100644 --- a/src/gui/widgets/progressindicator.h +++ b/src/gui/widgets/progressindicator.h @@ -20,6 +20,8 @@ #pragma once +#include "resources/imageset.h" + #include <guichan/widget.hpp> #include <memory> @@ -41,4 +43,5 @@ public: private: std::unique_ptr<SimpleAnimation> mIndicator; + ResourceRef<ImageSet> mImageSet; }; diff --git a/src/gui/widgets/radiobutton.cpp b/src/gui/widgets/radiobutton.cpp index 92cdacd1..9fcc34fa 100644 --- a/src/gui/widgets/radiobutton.cpp +++ b/src/gui/widgets/radiobutton.cpp @@ -29,12 +29,12 @@ int RadioButton::instances = 0; float RadioButton::mAlpha = 1.0; -Image *RadioButton::radioNormal; -Image *RadioButton::radioChecked; -Image *RadioButton::radioDisabled; -Image *RadioButton::radioDisabledChecked; -Image *RadioButton::radioNormalHi; -Image *RadioButton::radioCheckedHi; +ResourceRef<Image> RadioButton::radioNormal; +ResourceRef<Image> RadioButton::radioChecked; +ResourceRef<Image> RadioButton::radioDisabled; +ResourceRef<Image> RadioButton::radioDisabledChecked; +ResourceRef<Image> RadioButton::radioNormalHi; +ResourceRef<Image> RadioButton::radioCheckedHi; RadioButton::RadioButton(const std::string &caption, const std::string &group, bool marked): @@ -65,12 +65,12 @@ RadioButton::~RadioButton() if (instances == 0) { - radioNormal->decRef(); - radioChecked->decRef(); - radioDisabled->decRef(); - radioDisabledChecked->decRef(); - radioNormalHi->decRef(); - radioCheckedHi->decRef(); + radioNormal = nullptr; + radioChecked = nullptr; + radioDisabled = nullptr; + radioDisabledChecked = nullptr; + radioNormalHi = nullptr; + radioCheckedHi = nullptr; } } diff --git a/src/gui/widgets/radiobutton.h b/src/gui/widgets/radiobutton.h index b8026e56..4f6b5a49 100644 --- a/src/gui/widgets/radiobutton.h +++ b/src/gui/widgets/radiobutton.h @@ -21,6 +21,8 @@ #pragma once +#include "resources/resource.h" + #include <guichan/widgets/radiobutton.hpp> class Image; @@ -61,10 +63,10 @@ class RadioButton : public gcn::RadioButton static int instances; static float mAlpha; bool mHasMouse = false; - static Image *radioNormal; - static Image *radioChecked; - static Image *radioDisabled; - static Image *radioDisabledChecked; - static Image *radioNormalHi; - static Image *radioCheckedHi; + static ResourceRef<Image> radioNormal; + static ResourceRef<Image> radioChecked; + static ResourceRef<Image> radioDisabled; + static ResourceRef<Image> radioDisabledChecked; + static ResourceRef<Image> radioNormalHi; + static ResourceRef<Image> radioCheckedHi; }; diff --git a/src/gui/widgets/resizegrip.cpp b/src/gui/widgets/resizegrip.cpp index dd29a977..b7d2ec16 100644 --- a/src/gui/widgets/resizegrip.cpp +++ b/src/gui/widgets/resizegrip.cpp @@ -29,7 +29,7 @@ #include <guichan/graphics.hpp> -Image *ResizeGrip::gripImage = nullptr; +ResourceRef<Image> ResizeGrip::gripImage; int ResizeGrip::mInstances = 0; float ResizeGrip::mAlpha = 1.0; @@ -53,7 +53,7 @@ ResizeGrip::~ResizeGrip() mInstances--; if (mInstances == 0) - gripImage->decRef(); + gripImage = nullptr; } void ResizeGrip::draw(gcn::Graphics *graphics) diff --git a/src/gui/widgets/resizegrip.h b/src/gui/widgets/resizegrip.h index 7d496950..5638eff4 100644 --- a/src/gui/widgets/resizegrip.h +++ b/src/gui/widgets/resizegrip.h @@ -21,6 +21,8 @@ #pragma once +#include "resources/resource.h" + #include <guichan/widget.hpp> class Image; @@ -36,7 +38,6 @@ class ResizeGrip : public gcn::Widget { public: ResizeGrip(const std::string &image = "resize.png"); - ~ResizeGrip() override; /** @@ -45,7 +46,7 @@ class ResizeGrip : public gcn::Widget void draw(gcn::Graphics *graphics) override; private: - static Image *gripImage; /**< Resize grip image */ + static ResourceRef<Image> gripImage; /**< Resize grip image */ static int mInstances; /**< Number of resize grip instances */ static float mAlpha; }; diff --git a/src/gui/widgets/scrollarea.cpp b/src/gui/widgets/scrollarea.cpp index 225a231d..44065580 100644 --- a/src/gui/widgets/scrollarea.cpp +++ b/src/gui/widgets/scrollarea.cpp @@ -34,7 +34,7 @@ float ScrollArea::mAlpha = 1.0; ImageRect ScrollArea::background; ImageRect ScrollArea::vMarker; ImageRect ScrollArea::vMarkerHi; -Image *ScrollArea::buttons[4][2]; +ResourceRef<Image> ScrollArea::buttons[4][2]; ScrollArea::ScrollArea() { @@ -60,14 +60,14 @@ ScrollArea::~ScrollArea() std::for_each(vMarker.grid, vMarker.grid + 9, dtor<Image*>()); std::for_each(vMarkerHi.grid, vMarkerHi.grid + 9, dtor<Image*>()); - buttons[UP][0]->decRef(); - buttons[UP][1]->decRef(); - buttons[DOWN][0]->decRef(); - buttons[DOWN][1]->decRef(); - buttons[LEFT][0]->decRef(); - buttons[LEFT][1]->decRef(); - buttons[RIGHT][0]->decRef(); - buttons[RIGHT][1]->decRef(); + buttons[UP][0] = nullptr; + buttons[UP][1] = nullptr; + buttons[DOWN][0] = nullptr; + buttons[DOWN][1] = nullptr; + buttons[LEFT][0] = nullptr; + buttons[LEFT][1] = nullptr; + buttons[RIGHT][0] = nullptr; + buttons[RIGHT][1] = nullptr; } } @@ -84,7 +84,7 @@ void ScrollArea::init() if (instances == 0) { // Load the background skin - Image *textbox = Theme::getImageFromTheme("deepbox.png"); + auto textbox = Theme::getImageFromTheme("deepbox.png"); const int bggridx[4] = {0, 3, 28, 31}; const int bggridy[4] = {0, 3, 28, 31}; int a = 0; @@ -102,11 +102,9 @@ void ScrollArea::init() } background.setAlpha(config.guiAlpha); - textbox->decRef(); - // Load vertical scrollbar skin - Image *vscroll = Theme::getImageFromTheme("vscroll_grey.png"); - Image *vscrollHi = Theme::getImageFromTheme("vscroll_highlight.png"); + auto vscroll = Theme::getImageFromTheme("vscroll_grey.png"); + auto vscrollHi = Theme::getImageFromTheme("vscroll_highlight.png"); int vsgridx[4] = {0, 4, 7, 11}; int vsgridy[4] = {0, 4, 15, 19}; @@ -131,9 +129,6 @@ void ScrollArea::init() vMarker.setAlpha(config.guiAlpha); vMarkerHi.setAlpha(config.guiAlpha); - vscroll->decRef(); - vscrollHi->decRef(); - buttons[UP][0] = Theme::getImageFromTheme("vscroll_up_default.png"); buttons[DOWN][0] = diff --git a/src/gui/widgets/scrollarea.h b/src/gui/widgets/scrollarea.h index ef20610b..faec0e8f 100644 --- a/src/gui/widgets/scrollarea.h +++ b/src/gui/widgets/scrollarea.h @@ -21,8 +21,10 @@ #pragma once -#include <guichan/widgets/scrollarea.hpp> +#include "resources/resource.h" + #include <guichan/widgetlistener.hpp> +#include <guichan/widgets/scrollarea.hpp> class Image; class ImageRect; @@ -133,7 +135,7 @@ class ScrollArea : public gcn::ScrollArea, public gcn::WidgetListener static ImageRect background; static ImageRect vMarker; static ImageRect vMarkerHi; - static Image *buttons[4][2]; + static ResourceRef<Image> buttons[4][2]; int mX = 0; int mY = 0; diff --git a/src/gui/widgets/shortcutcontainer.cpp b/src/gui/widgets/shortcutcontainer.cpp index 5925752e..4bcf3860 100644 --- a/src/gui/widgets/shortcutcontainer.cpp +++ b/src/gui/widgets/shortcutcontainer.cpp @@ -21,11 +21,7 @@ #include "gui/widgets/shortcutcontainer.h" -float ShortcutContainer::mAlpha = 1.0; - -ShortcutContainer::ShortcutContainer() -{ -} +ShortcutContainer::~ShortcutContainer() = default; void ShortcutContainer::widgetResized(const gcn::Event &event) { diff --git a/src/gui/widgets/shortcutcontainer.h b/src/gui/widgets/shortcutcontainer.h index dd88bded..9998e188 100644 --- a/src/gui/widgets/shortcutcontainer.h +++ b/src/gui/widgets/shortcutcontainer.h @@ -21,12 +21,12 @@ #pragma once +#include "resources/image.h" + #include <guichan/mouselistener.hpp> #include <guichan/widget.hpp> #include <guichan/widgetlistener.hpp> -class Image; - /** * A generic shortcut container. * @@ -37,7 +37,8 @@ class ShortcutContainer : public gcn::Widget, public gcn::MouseListener { public: - ShortcutContainer(); + ShortcutContainer() = default; + ~ShortcutContainer(); /** * Draws the shortcuts @@ -69,9 +70,7 @@ class ShortcutContainer : public gcn::Widget, */ int getIndexFromGrid(int pointX, int pointY) const; - Image *mBackgroundImg; - - static float mAlpha; + ResourceRef<Image> mBackgroundImg; int mMaxItems = 0; int mBoxWidth = 0; diff --git a/src/gui/widgets/slider.cpp b/src/gui/widgets/slider.cpp index a7ba37e8..f0b61667 100644 --- a/src/gui/widgets/slider.cpp +++ b/src/gui/widgets/slider.cpp @@ -79,8 +79,8 @@ void Slider::init() // Load resources if (mInstances == 0) { - Image *slider = Theme::getImageFromTheme("slider.png"); - Image *sliderHi = Theme::getImageFromTheme("slider_hilight.png"); + auto slider = Theme::getImageFromTheme("slider.png"); + auto sliderHi = Theme::getImageFromTheme("slider_hilight.png"); x = 0; y = 0; w = 15; h = 6; @@ -111,9 +111,6 @@ void Slider::init() w = 9; h = 10; vGrip = slider->getSubImage(x, y, w, h); vGripHi = sliderHi->getSubImage(x, y, w, h); - - slider->decRef(); - sliderHi->decRef(); } mInstances++; @@ -205,4 +202,3 @@ void Slider::mouseExited(gcn::MouseEvent& event) { mHasMouse = false; } - diff --git a/src/gui/widgets/tab.cpp b/src/gui/widgets/tab.cpp index 64f49eac..d20e7993 100644 --- a/src/gui/widgets/tab.cpp +++ b/src/gui/widgets/tab.cpp @@ -88,17 +88,15 @@ void Tab::init() if (mInstances == 0) { // Load the skin - Image *tab[TAB_COUNT]; - for (int mode = 0; mode < TAB_COUNT; mode++) { - tab[mode] = Theme::getImageFromTheme(data[mode].file); + auto tabImage = Theme::getImageFromTheme(data[mode].file); int a = 0; for (int y = 0; y < 3; y++) { for (int x = 0; x < 3; x++) { - tabImg[mode].grid[a] = tab[mode]->getSubImage( + tabImg[mode].grid[a] = tabImage->getSubImage( data[mode].gridX[x], data[mode].gridY[y], data[mode].gridX[x + 1] - data[mode].gridX[x] + 1, data[mode].gridY[y + 1] - data[mode].gridY[y] + 1); @@ -106,7 +104,6 @@ void Tab::init() } } tabImg[mode].setAlpha(mAlpha); - tab[mode]->decRef(); } } mInstances++; diff --git a/src/gui/widgets/textfield.cpp b/src/gui/widgets/textfield.cpp index dd0adecd..be75a96f 100644 --- a/src/gui/widgets/textfield.cpp +++ b/src/gui/widgets/textfield.cpp @@ -53,7 +53,7 @@ TextField::TextField(const std::string &text, bool loseFocusOnTab): if (instances == 0) { // Load the skin - Image *textbox = Theme::getImageFromTheme("deepbox.png"); + auto textbox = Theme::getImageFromTheme("deepbox.png"); int gridx[4] = {0, 3, 28, 31}; int gridy[4] = {0, 3, 28, 31}; int a = 0; @@ -70,8 +70,6 @@ TextField::TextField(const std::string &text, bool loseFocusOnTab): } } skin.setAlpha(config.guiAlpha); - - textbox->decRef(); } instances++; diff --git a/src/imageparticle.cpp b/src/imageparticle.cpp index 9cde91d0..86f955c0 100644 --- a/src/imageparticle.cpp +++ b/src/imageparticle.cpp @@ -27,17 +27,12 @@ ImageParticle::ImageParticle(Map *map, Image *image): Particle(map), - mImage(image) + mImageRef(image) { - if (mImage) - mImage->incRef(); + mImage = mImageRef; } -ImageParticle::~ImageParticle() -{ - if (mImage) - mImage->decRef(); -} +ImageParticle::~ImageParticle() = default; bool ImageParticle::draw(Graphics *graphics, int offsetX, int offsetY) const { diff --git a/src/imageparticle.h b/src/imageparticle.h index 5e968a38..0f135c33 100644 --- a/src/imageparticle.h +++ b/src/imageparticle.h @@ -23,6 +23,8 @@ #include "particle.h" +#include "resources/resource.h" + class Image; class Map; @@ -49,4 +51,5 @@ class ImageParticle : public Particle protected: Image *mImage; /**< The image used for this particle. */ + ResourceRef<Image> mImageRef; }; diff --git a/src/item.cpp b/src/item.cpp index cae545b1..120c55a7 100644 --- a/src/item.cpp +++ b/src/item.cpp @@ -48,12 +48,10 @@ void Item::setId(int id) mImage = resman->getImage(paths.getStringValue("itemIcons") + display.image); if (!mImage) + { mImage = Theme::getImageFromTheme(paths.getValue("unknownItemFile", "unknown-item.png")); - - // Remove the automatic reference added by the ResourceManager - if (mImage) - mImage->decRef(); + } } void Item::doEvent(Event::Type eventName) diff --git a/src/map.cpp b/src/map.cpp index 929d66f8..835d400d 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -245,7 +245,7 @@ void Map::initializeAmbientLayers() auto addAmbientLayer = [=](const std::string &name, std::vector<AmbientLayer> &list) { - if (auto img = resman->getImageRef(getProperty(name + "image"))) + if (auto img = resman->getImage(getProperty(name + "image"))) { auto &ambientLayer = list.emplace_back(img); ambientLayer.mParallax = getFloatProperty(name + "parallax"); diff --git a/src/particle.cpp b/src/particle.cpp index 6717653f..cb79c86f 100644 --- a/src/particle.cpp +++ b/src/particle.cpp @@ -294,7 +294,7 @@ Particle *Particle::addEffect(const std::string &particleEffectFile, if (!imageSrc.empty() && !dyePalettes.empty()) Dye::instantiate(imageSrc, dyePalettes); - auto img = resman->getImageRef(imageSrc); + auto img = resman->getImage(imageSrc); newParticle = new ImageParticle(mMap, img); } // Other diff --git a/src/particleemitter.cpp b/src/particleemitter.cpp index a436d913..4954d317 100644 --- a/src/particleemitter.cpp +++ b/src/particleemitter.cpp @@ -29,11 +29,8 @@ #include "resources/dye.h" #include "resources/image.h" -#include "resources/imageset.h" #include "resources/resourcemanager.h" -#include "utils/stringutils.h" - #include <cmath> #define SIN45 0.707106781f @@ -102,7 +99,7 @@ ParticleEmitter::ParticleEmitter(XML::Node emitterNode, Particle *target, Dye::instantiate(image, dyePalettes); ResourceManager *resman = ResourceManager::getInstance(); - mParticleImage = resman->getImageRef(image); + mParticleImage = resman->getImage(image); } } else if (name == "horizontal-angle") @@ -195,150 +192,13 @@ ParticleEmitter::ParticleEmitter(XML::Node emitterNode, Particle *target, } else if (propertyNode.name() == "rotation") { - ImageSet *imageset = ResourceManager::getInstance()->getImageSet( - propertyNode.getProperty("imageset", ""), - propertyNode.getProperty("width", 0), - propertyNode.getProperty("height", 0) - ); - - // Get animation frames - for (auto frameNode : propertyNode.children()) - { - int delay = frameNode.getProperty("delay", 0); - int offsetX = frameNode.getProperty("offsetX", 0); - int offsetY = frameNode.getProperty("offsetY", 0); - if (mMap) - { - offsetX -= imageset->getWidth() / 2 - - mMap->getTileWidth() / 2; - offsetY -= imageset->getHeight() - mMap->getTileHeight(); - } - - if (frameNode.name() == "frame") - { - int index = frameNode.getProperty("index", -1); - - if (index < 0) - { - logger->log("No valid value for 'index'"); - continue; - } - - Image *img = imageset->get(index); - - if (!img) - { - logger->log("No image at index %d", index); - continue; - } - - mParticleRotation.addFrame(img, delay, offsetX, offsetY); - } - else if (frameNode.name() == "sequence") - { - int start = frameNode.getProperty("start", -1); - int end = frameNode.getProperty("end", -1); - - if (start < 0 || end < 0) - { - logger->log("No valid value for 'start' or 'end'"); - continue; - } - - while (end >= start) - { - Image *img = imageset->get(start); - - if (!img) - { - logger->log("No image at index %d", start); - continue; - } - - mParticleRotation.addFrame(img, delay, offsetX, offsetY); - start++; - } - } - else if (frameNode.name() == "end") - { - mParticleRotation.addTerminator(); - } - } // for frameNode + mParticleRotation = Animation::fromXML(propertyNode); } else if (propertyNode.name() == "animation") { - std::string imagesetPath = - propertyNode.getProperty("imageset", ""); - ImageSet *imageset = ResourceManager::getInstance()->getImageSet( - imagesetPath, - propertyNode.getProperty("width", 0), - propertyNode.getProperty("height", 0) - ); - - if (!imageset) - logger->error(strprintf("Failed to load \"%s\"", - imagesetPath.c_str())); - - // Get animation frames - for (auto frameNode : propertyNode.children()) - { - int delay = frameNode.getProperty("delay", 0); - int offsetX = frameNode.getProperty("offsetX", 0); - int offsetY = frameNode.getProperty("offsetY", 0); - offsetY -= imageset->getHeight() - 32; - offsetX -= imageset->getWidth() / 2 - 16; - - if (frameNode.name() == "frame") - { - int index = frameNode.getProperty("index", -1); - - if (index < 0) - { - logger->log("No valid value for 'index'"); - continue; - } - - Image *img = imageset->get(index); - - if (!img) - { - logger->log("No image at index %d", index); - continue; - } - - mParticleAnimation.addFrame(img, delay, offsetX, offsetY); - } - else if (frameNode.name() == "sequence") - { - int start = frameNode.getProperty("start", -1); - int end = frameNode.getProperty("end", -1); - - if (start < 0 || end < 0) - { - logger->log("No valid value for 'start' or 'end'"); - continue; - } - - while (end >= start) - { - Image *img = imageset->get(start); - - if (!img) - { - logger->log("No image at index %d", start); - continue; - } - - mParticleAnimation.addFrame(img, delay, offsetX, offsetY); - start++; - } - } - else if (frameNode.name() == "end") - { - mParticleAnimation.addTerminator(); - } - } // for frameNode - } else if (propertyNode.name() == "deatheffect") + mParticleAnimation = Animation::fromXML(propertyNode); + } + else if (propertyNode.name() == "deatheffect") { mDeathEffect = propertyNode.textContent(); mDeathEffectConditions = 0x00; diff --git a/src/resources/animation.cpp b/src/resources/animation.cpp index b48e8cff..91c22236 100644 --- a/src/resources/animation.cpp +++ b/src/resources/animation.cpp @@ -21,6 +21,11 @@ #include "resources/animation.h" +#include "dye.h" +#include "game.h" +#include "log.h" +#include "resourcemanager.h" + void Animation::addFrame(Image *image, int delay, int offsetX, int offsetY) { auto &frame = mFrames.emplace_back(); @@ -41,3 +46,90 @@ bool Animation::isTerminator(const Frame &candidate) { return candidate.image == nullptr; } + +Animation Animation::fromXML(XML::Node node, const std::string &dyePalettes) +{ + Animation animation; + + std::string imagePath = node.getProperty("imageset", std::string()); + + // Instanciate the dye coloration. + Dye::instantiate(imagePath, dyePalettes); + + auto imageSet = ResourceManager::getInstance()->getImageSet( + imagePath, + node.getProperty("width", 0), + node.getProperty("height", 0) + ); + + if (!imageSet) + return animation; + + // Get animation frames + for (auto frameNode : node.children()) + { + int delay = frameNode.getProperty("delay", 0); + int offsetX = frameNode.getProperty("offsetX", 0); + int offsetY = frameNode.getProperty("offsetY", 0); + Game *game = Game::instance(); + if (game) + { + offsetX -= imageSet->getWidth() / 2 - game->getCurrentTileWidth() / 2; + offsetY -= imageSet->getHeight() - game->getCurrentTileHeight(); + } + + if (frameNode.name() == "frame") + { + int index = frameNode.getProperty("index", -1); + + if (index < 0) + { + logger->log("No valid value for 'index'"); + continue; + } + + Image *img = imageSet->get(index); + + if (!img) + { + logger->log("No image at index %d", index); + continue; + } + + animation.addFrame(img, delay, offsetX, offsetY); + } + else if (frameNode.name() == "sequence") + { + int start = frameNode.getProperty("start", -1); + int end = frameNode.getProperty("end", -1); + + if (start < 0 || end < 0) + { + logger->log("No valid value for 'start' or 'end'"); + continue; + } + + while (end >= start) + { + Image *img = imageSet->get(start); + + if (!img) + { + logger->log("No image at index %d", start); + continue; + } + + animation.addFrame(img, delay, offsetX, offsetY); + start++; + } + } + else if (frameNode.name() == "end") + { + animation.addTerminator(); + } + } + + animation.mImageSet = imageSet; + + return animation; +} diff --git a/src/resources/animation.h b/src/resources/animation.h index 01b8b388..cc3abd58 100644 --- a/src/resources/animation.h +++ b/src/resources/animation.h @@ -21,6 +21,9 @@ #pragma once +#include "resources/imageset.h" +#include "utils/xml.h" + #include <vector> class Image; @@ -76,7 +79,14 @@ class Animation final */ static bool isTerminator(const Frame &phase); + /** + * Loads an animation from XML. + */ + static Animation fromXML(XML::Node node, + const std::string &dyePalettes = {}); + protected: + ResourceRef<ImageSet> mImageSet; std::vector<Frame> mFrames; int mDuration = 0; }; diff --git a/src/resources/emotedb.cpp b/src/resources/emotedb.cpp index 53038e12..d29483d1 100644 --- a/src/resources/emotedb.cpp +++ b/src/resources/emotedb.cpp @@ -43,7 +43,7 @@ void EmoteDB::init() mUnknown.name = "unknown"; mUnknown.effectId = -1; - mUnknown.image = ResourceManager::getInstance()->getImageRef("graphics/sprites/error.png"); + mUnknown.image = ResourceManager::getInstance()->getImage("graphics/sprites/error.png"); } void EmoteDB::readEmoteNode(XML::Node node, const std::string &filename) @@ -82,7 +82,6 @@ void EmoteDB::readEmoteNode(XML::Node node, const std::string &filename) emote.is = ResourceManager::getInstance()->getImageSet(imageName, width, height); - emote.is->decRef(); // clear automatic reference if (!emote.is || emote.is->size() == 0) { diff --git a/src/resources/mapreader.cpp b/src/resources/mapreader.cpp index b5a5e258..b952cdcc 100644 --- a/src/resources/mapreader.cpp +++ b/src/resources/mapreader.cpp @@ -527,7 +527,7 @@ static Tileset *readTileset(XML::Node node, const std::string &path, std::string sourceStr = resolveRelativePath(pathDir, source); ResourceManager *resman = ResourceManager::getInstance(); - auto tilebmp = resman->getImageRef(sourceStr); + auto tilebmp = resman->getImage(sourceStr); if (tilebmp) { diff --git a/src/resources/music.cpp b/src/resources/music.cpp index 12c723bd..069af588 100644 --- a/src/resources/music.cpp +++ b/src/resources/music.cpp @@ -33,7 +33,7 @@ Music::~Music() Mix_FreeMusic(mMusic); } -Resource *Music::load(SDL_RWops *rw) +Music *Music::load(SDL_RWops *rw) { if (Mix_Music *music = Mix_LoadMUS_RW(rw, 1)) { diff --git a/src/resources/music.h b/src/resources/music.h index 81dc0d19..d22257da 100644 --- a/src/resources/music.h +++ b/src/resources/music.h @@ -38,10 +38,10 @@ class Music : public Resource * * @param rw The SDL_RWops to load the music data from. * - * @return <code>NULL</code> if the an error occurred, a valid pointer - * otherwise. + * @return <code>nullptr</code> if the an error occurred, a valid + * pointer otherwise. */ - static Resource *load(SDL_RWops *rw); + static Music *load(SDL_RWops *rw); /** * Plays the music. diff --git a/src/resources/resource.h b/src/resources/resource.h index c6b41295..ba8d17cc 100644 --- a/src/resources/resource.h +++ b/src/resources/resource.h @@ -150,8 +150,12 @@ public: * This is currently necessary to avoid calls to decRef on instances of * SubImage, which are not reference counted resources. */ - void release() - { mResource = nullptr; } + RESOURCE *release() + { + RESOURCE *resource = mResource; + mResource = nullptr; + return resource; + } private: RESOURCE *mResource; diff --git a/src/resources/resourcemanager.cpp b/src/resources/resourcemanager.cpp index c64786ab..5045d21b 100644 --- a/src/resources/resourcemanager.cpp +++ b/src/resources/resourcemanager.cpp @@ -44,56 +44,40 @@ ResourceManager *ResourceManager::instance = nullptr; ResourceManager::ResourceManager() - : mOldestOrphan(0) { logger->log("Initializing resource manager..."); } ResourceManager::~ResourceManager() { + // Put the orphaned resources into the main list for cleanup mResources.insert(mOrphanedResources.begin(), mOrphanedResources.end()); - // Release any remaining spritedefs first because they depend on image sets - auto iter = mResources.begin(); - while (iter != mResources.end()) + auto cleanupResources = [&](auto match) { - if (dynamic_cast<SpriteDef*>(iter->second) != nullptr) + for (auto iter = mResources.begin(); iter != mResources.end(); ) { - cleanUp(iter->second); - auto toErase = iter; - ++iter; - mResources.erase(toErase); - } - else - { - ++iter; + if (match(iter->second)) + { + cleanUp(iter->second); + iter = mResources.erase(iter); + } + else + { + ++iter; + } } - } + }; - // Release any remaining image sets first because they depend on images - iter = mResources.begin(); - while (iter != mResources.end()) - { - if (dynamic_cast<ImageSet*>(iter->second) != nullptr) - { - cleanUp(iter->second); - auto toErase = iter; - ++iter; - mResources.erase(toErase); - } - else - { - ++iter; - } - } + // SpriteDef references ImageSet + cleanupResources([](Resource *res) { return dynamic_cast<SpriteDef *>(res); }); - // Release remaining resources, logging the number of dangling references. - iter = mResources.begin(); - while (iter != mResources.end()) - { - cleanUp(iter->second); - ++iter; - } + // ImageSet references Image + cleanupResources([](Resource *res) { return dynamic_cast<ImageSet *>(res); }); + + // Release remaining resources + for (const auto &resource : mResources) + cleanUp(resource.second); } void ResourceManager::cleanUp(Resource *res) @@ -162,7 +146,7 @@ void ResourceManager::searchAndAddArchives(const std::string &path, { const size_t len = strlen(fileName); - if (len > ext.length() && !ext.compare(fileName + (len - ext.length()))) + if (len > ext.length() && ext != (fileName + (len - ext.length()))) { std::string file = path + fileName; if (auto realDir = FS::getRealDir(file)) @@ -200,7 +184,6 @@ Resource *ResourceManager::get(const std::string &idPath, auto resIter = mResources.find(idPath); if (resIter != mResources.end()) { - resIter->second->incRef(); return resIter->second; } @@ -210,53 +193,41 @@ Resource *ResourceManager::get(const std::string &idPath, Resource *res = resIter->second; mResources.insert(*resIter); mOrphanedResources.erase(resIter); - res->incRef(); return res; } Resource *resource = generator(); - if (resource) { - resource->incRef(); resource->mIdPath = idPath; mResources[idPath] = resource; cleanOrphans(); } - // Returns NULL if the object could not be created. return resource; } -Resource *ResourceManager::get(const std::string &path, loader fun) +ResourceRef<Music> ResourceManager::getMusic(const std::string &path) { - return get(path, [&] () -> Resource * { - // - // We use a buffered SDL_RWops to workaround a performance issue when - // SDL_mixer is using stb_vorbis. The overhead of calling - // PHYSFS_readBytes each time is too high because stb_vorbis requests - // the file one byte at a time. - // - // See https://github.com/libsdl-org/SDL_mixer/issues/670 - // + return static_cast<Music*>(get(path, [&] () -> Resource * { if (SDL_RWops *rw = FS::openBufferedRWops(path)) - return fun(rw); + return Music::load(rw); return nullptr; - }); + })); } -Music *ResourceManager::getMusic(const std::string &idPath) +ResourceRef<SoundEffect> ResourceManager::getSoundEffect(const std::string &path) { - return static_cast<Music*>(get(idPath, Music::load)); -} + return static_cast<SoundEffect*>(get(path, [&] () -> Resource * { + if (SDL_RWops *rw = FS::openBufferedRWops(path)) + return SoundEffect::load(rw); -SoundEffect *ResourceManager::getSoundEffect(const std::string &idPath) -{ - return static_cast<SoundEffect*>(get(idPath, SoundEffect::load)); + return nullptr; + })); } -Image *ResourceManager::getImage(const std::string &idPath) +ResourceRef<Image> ResourceManager::getImage(const std::string &idPath) { return static_cast<Image*>(get(idPath, [&] () -> Resource * { std::string path = idPath; @@ -277,21 +248,14 @@ Image *ResourceManager::getImage(const std::string &idPath) })); } -ResourceRef<Image> ResourceManager::getImageRef(const std::string &idPath) -{ - ResourceRef<Image> img = getImage(idPath); - img->decRef(); // remove ref added by ResourceManager::get - return img; -} - -ImageSet *ResourceManager::getImageSet(const std::string &imagePath, - int w, int h) +ResourceRef<ImageSet> ResourceManager::getImageSet(const std::string &imagePath, + int w, int h) { std::stringstream ss; ss << imagePath << "[" << w << "x" << h << "]"; return static_cast<ImageSet*>(get(ss.str(), [&] () -> Resource * { - auto img = getImageRef(imagePath); + auto img = getImage(imagePath); if (!img) return nullptr; @@ -299,7 +263,7 @@ ImageSet *ResourceManager::getImageSet(const std::string &imagePath, })); } -SpriteDef *ResourceManager::getSprite(const std::string &path, int variant) +ResourceRef<SpriteDef> ResourceManager::getSprite(const std::string &path, int variant) { std::stringstream ss; ss << path << "[" << variant << "]"; diff --git a/src/resources/resourcemanager.h b/src/resources/resourcemanager.h index 5464ff3d..728a9b74 100644 --- a/src/resources/resourcemanager.h +++ b/src/resources/resourcemanager.h @@ -34,8 +34,6 @@ class Music; class SoundEffect; class SpriteDef; -struct SDL_RWops; - /** * A class for loading and managing resources. */ @@ -44,9 +42,6 @@ class ResourceManager friend class Resource; public: - - using loader = Resource *(*)(SDL_RWops *); - ResourceManager(); /** @@ -81,62 +76,31 @@ class ResourceManager static std::string getPath(const std::string &file); /** - * Creates a resource and adds it to the resource map. - * - * @param idPath The resource identifier path. - * @param fun A function for generating the resource. - * @param data Extra parameters for the generator. - * @return A valid resource or <code>NULL</code> if the resource could - * not be generated. - */ - Resource *get(const std::string &idPath, - const std::function<Resource *()> &generator); - - /** - * Loads a resource from a file and adds it to the resource map. - * - * @param path The file name. - * @param fun A function for parsing the file. - * @return A valid resource or <code>NULL</code> if the resource could - * not be loaded. - */ - Resource *get(const std::string &path, loader fun); - - /** - * Convenience wrapper around ResourceManager::get for loading - * images. - */ - Image *getImage(const std::string &idPath); - - /** - * Convenience wrapper around ResourceManager::get for loading - * images. Returns an automatically reference-counted resource. + * Loads the Image resource found at the given identifier path. The + * path can include a dye specification after a '|' character. */ - ResourceRef<Image> getImageRef(const std::string &idPath); + ResourceRef<Image> getImage(const std::string &idPath); /** - * Convenience wrapper around ResourceManager::get for loading - * songs. + * Loads the Music resource found at the given path. */ - Music *getMusic(const std::string &idPath); + ResourceRef<Music> getMusic(const std::string &path); /** - * Convenience wrapper around ResourceManager::get for loading - * samples. + * Loads the SoundEffect resource found at the given path. */ - SoundEffect *getSoundEffect(const std::string &idPath); + ResourceRef<SoundEffect> getSoundEffect(const std::string &path); /** - * Creates a image set based on the image referenced by the given - * path and the supplied sprite sizes + * Loads a image set based on the image referenced by the given path + * and the supplied sprite sizes. */ - ImageSet *getImageSet(const std::string &imagePath, int w, int h); + ResourceRef<ImageSet> getImageSet(const std::string &imagePath, int w, int h); /** - * Creates a sprite definition based on a given path and the supplied - * variant. + * Loads a SpriteDef based on a given path and the supplied variant. */ - SpriteDef *getSprite(const std::string &path, int variant = 0); + ResourceRef<SpriteDef> getSprite(const std::string &path, int variant = 0); /** * Returns an instance of the class, creating one if it does not @@ -151,6 +115,19 @@ class ResourceManager private: /** + * Looks up a resource, creating it with the generator function if it + * does not exist. Does not increment the reference count of the + * resource. + * + * @param idPath The resource identifier path. + * @param generator A function for generating the resource. + * @return A valid resource or <code>nullptr</code> if the resource could + * not be generated. + */ + Resource *get(const std::string &idPath, + const std::function<Resource *()> &generator); + + /** * Releases a resource, placing it in the set of orphaned resources. * Only called from Resource::decRef, */ @@ -172,5 +149,5 @@ class ResourceManager static ResourceManager *instance; std::map<std::string, Resource *> mResources; std::map<std::string, Resource *> mOrphanedResources; - time_t mOldestOrphan; + time_t mOldestOrphan = 0; }; diff --git a/src/resources/soundeffect.cpp b/src/resources/soundeffect.cpp index 8f8cdfc5..19d7a820 100644 --- a/src/resources/soundeffect.cpp +++ b/src/resources/soundeffect.cpp @@ -28,23 +28,20 @@ SoundEffect::~SoundEffect() Mix_FreeChunk(mChunk); } -Resource *SoundEffect::load(SDL_RWops *rw) +SoundEffect *SoundEffect::load(SDL_RWops *rw) { // Load the music data and free the RWops structure - Mix_Chunk *tmpSoundEffect = Mix_LoadWAV_RW(rw, 1); - - if (tmpSoundEffect) + if (Mix_Chunk *soundEffect = Mix_LoadWAV_RW(rw, 1)) { - return new SoundEffect(tmpSoundEffect); + return new SoundEffect(soundEffect); } logger->log("Error, failed to load sound effect: %s", Mix_GetError()); return nullptr; } -bool SoundEffect::play(int loops, int volume, int channel) +int SoundEffect::play(int loops, int volume, int channel) { Mix_VolumeChunk(mChunk, volume); - - return Mix_PlayChannel(channel, mChunk, loops) != -1; + return Mix_PlayChannel(channel, mChunk, loops); } diff --git a/src/resources/soundeffect.h b/src/resources/soundeffect.h index 127b1ce6..9ca8490d 100644 --- a/src/resources/soundeffect.h +++ b/src/resources/soundeffect.h @@ -36,12 +36,12 @@ class SoundEffect : public Resource /** * Loads a sample from a buffer in memory. * - * @param rw The SDL_RWops to load the sample data from. + * @param rw The SDL_RWops to load the sample data from. * - * @return <code>NULL</code> if the an error occurred, a valid pointer + * @return <code>nullptr</code> if the an error occurred, a valid pointer * otherwise. */ - static Resource *load(SDL_RWops *rw); + static SoundEffect *load(SDL_RWops *rw); /** * Plays the sample. @@ -50,10 +50,10 @@ class SoundEffect : public Resource * @param volume Sample playback volume. * @param channel Sample playback channel. * - * @return <code>true</code> if the playback started properly - * <code>false</code> otherwise. + * @return which channel was used to play the sound, or -1 if sound could not + * be played. */ - bool play(int loops, int volume, int channel = -1); + int play(int loops, int volume, int channel = -1); protected: SoundEffect(Mix_Chunk *soundEffect): mChunk(soundEffect) {} diff --git a/src/resources/spritedef.cpp b/src/resources/spritedef.cpp index f42e623e..85e5e566 100644 --- a/src/resources/spritedef.cpp +++ b/src/resources/spritedef.cpp @@ -28,7 +28,6 @@ #include "resources/animation.h" #include "resources/dye.h" #include "resources/image.h" -#include "resources/imageset.h" #include "resources/resourcemanager.h" #include "configuration.h" @@ -155,8 +154,7 @@ void SpriteDef::loadImageSet(XML::Node node, const std::string &palettes) Dye::instantiate(imageSrc, palettes); ResourceManager *resman = ResourceManager::getInstance(); - ImageSet *imageSet = resman->getImageSet(imageSrc, width, height); - + auto imageSet = resman->getImageSet(imageSrc, width, height); if (!imageSet) { logger->error(strprintf("Couldn't load imageset (%s)!", @@ -328,11 +326,6 @@ SpriteDef::~SpriteDef() { delete action; } - - for (auto &imageSet : mImageSets) - { - imageSet.second->decRef(); - } } SpriteDirection SpriteDef::makeSpriteDirection(const std::string &direction) diff --git a/src/resources/spritedef.h b/src/resources/spritedef.h index 0f6e9966..83bd9598 100644 --- a/src/resources/spritedef.h +++ b/src/resources/spritedef.h @@ -21,7 +21,7 @@ #pragma once -#include "resources/resource.h" +#include "resources/imageset.h" #include "utils/xml.h" @@ -146,6 +146,6 @@ class SpriteDef : public Resource */ void substituteAction(std::string complete, std::string with); - std::map<std::string, ImageSet *> mImageSets; + std::map<std::string, ResourceRef<ImageSet>> mImageSets; std::map<std::string, Action *> mActions; }; diff --git a/src/resources/theme.cpp b/src/resources/theme.cpp index 26ab3aed..fe25a31b 100644 --- a/src/resources/theme.cpp +++ b/src/resources/theme.cpp @@ -64,7 +64,6 @@ Skin::~Skin() for (auto img : mBorder.grid) delete img; - mCloseImage->decRef(); delete mStickyImageUp; delete mStickyImageDown; } @@ -233,7 +232,7 @@ Skin *Theme::readSkin(const std::string &filename) logger->log("Theme::load(): <skinset> defines '%s' as a skin image.", skinSetImage.c_str()); - Image *dBorders = Theme::getImageFromTheme(skinSetImage); + auto dBorders = Theme::getImageFromTheme(skinSetImage); ImageRect border; memset(&border, 0, sizeof(ImageRect)); @@ -299,16 +298,13 @@ Skin *Theme::readSkin(const std::string &filename) } } - dBorders->decRef(); - logger->log("Finished loading skin."); // Hard-coded for now until we update the above code to look for window buttons - Image *closeImage = Theme::getImageFromTheme("close_button.png"); - Image *sticky = Theme::getImageFromTheme("sticky_button.png"); + auto closeImage = Theme::getImageFromTheme("close_button.png"); + auto sticky = Theme::getImageFromTheme("sticky_button.png"); Image *stickyImageUp = sticky->getSubImage(0, 0, 15, 15); Image *stickyImageDown = sticky->getSubImage(15, 0, 15, 15); - sticky->decRef(); Skin *skin = new Skin(border, closeImage, stickyImageUp, stickyImageDown); skin->updateAlpha(mMinimumOpacity); @@ -369,19 +365,12 @@ std::string Theme::resolveThemePath(const std::string &path) return std::string(defaultThemePath) + "/" + path; } -Image *Theme::getImageFromTheme(const std::string &path) +ResourceRef<Image> Theme::getImageFromTheme(const std::string &path) { ResourceManager *resman = ResourceManager::getInstance(); return resman->getImage(resolveThemePath(path)); } -ImageSet *Theme::getImageSetFromTheme(const std::string &path, - int w, int h) -{ - ResourceManager *resman = ResourceManager::getInstance(); - return resman->getImageSet(resolveThemePath(path), w, h); -} - static int readColorType(const std::string &type) { static std::string colors[] = { diff --git a/src/resources/theme.h b/src/resources/theme.h index 13491e26..d914fb7f 100644 --- a/src/resources/theme.h +++ b/src/resources/theme.h @@ -27,6 +27,7 @@ #include "eventlistener.h" #include "gui/palette.h" +#include "resources/resource.h" #include <map> #include <string> @@ -77,10 +78,10 @@ class Skin int instances = 0; private: - ImageRect mBorder; /**< The window border and background */ - Image *mCloseImage; /**< Close Button Image */ - Image *mStickyImageUp; /**< Sticky Button Image */ - Image *mStickyImageDown; /**< Sticky Button Image */ + ImageRect mBorder; /**< The window border and background */ + ResourceRef<Image> mCloseImage; /**< Close Button Image */ + Image *mStickyImageUp; /**< Sticky Button Image */ + Image *mStickyImageDown; /**< Sticky Button Image */ }; class Theme : public Palette, public EventListener @@ -98,9 +99,7 @@ class Theme : public Palette, public EventListener */ static std::string resolveThemePath(const std::string &path); - static Image *getImageFromTheme(const std::string &path); - static ImageSet *getImageSetFromTheme(const std::string &path, - int w, int h); + static ResourceRef<Image> getImageFromTheme(const std::string &path); enum ThemePalette { TEXT, diff --git a/src/rotationalparticle.cpp b/src/rotationalparticle.cpp index bcdb9bad..cdd7de61 100644 --- a/src/rotationalparticle.cpp +++ b/src/rotationalparticle.cpp @@ -35,11 +35,7 @@ RotationalParticle::RotationalParticle(Map *map, XML::Node animationNode, mAnimation(animationNode, dyePalettes) {} -RotationalParticle::~RotationalParticle() -{ - // Prevent ImageParticle from decreasing the reference count of the image - mImage = nullptr; -} +RotationalParticle::~RotationalParticle() = default; bool RotationalParticle::update() { diff --git a/src/simpleanimation.cpp b/src/simpleanimation.cpp index 506714d2..1ef22d84 100644 --- a/src/simpleanimation.cpp +++ b/src/simpleanimation.cpp @@ -21,28 +21,32 @@ #include "simpleanimation.h" -#include "game.h" #include "graphics.h" -#include "log.h" #include "resources/animation.h" -#include "resources/dye.h" #include "resources/image.h" #include "resources/imageset.h" #include "resources/resourcemanager.h" -SimpleAnimation::SimpleAnimation(Animation animation): - mAnimation(std::move(animation)), - mCurrentFrame(mAnimation.getFrame(0)), - mInitialized(true) +SimpleAnimation::SimpleAnimation(Animation animation) + : mAnimation(std::move(animation)) + , mInitialized(true) { + if (mAnimation.getLength() > 0) + mCurrentFrame = mAnimation.getFrame(0); } SimpleAnimation::SimpleAnimation(XML::Node animationNode, const std::string &dyePalettes) { - initializeAnimation(animationNode, dyePalettes); - mCurrentFrame = mAnimation.getFrame(0); + if (animationNode) + { + mAnimation = Animation::fromXML(animationNode, dyePalettes); + mInitialized = true; + } + + if (mAnimation.getLength() > 0) + mCurrentFrame = mAnimation.getFrame(0); } bool SimpleAnimation::draw(Graphics *graphics, int posX, int posY) const @@ -90,103 +94,9 @@ void SimpleAnimation::update(int dt) } } -int SimpleAnimation::getLength() const -{ - return mAnimation.getLength(); -} - Image *SimpleAnimation::getCurrentImage() const { if (mCurrentFrame) return mCurrentFrame->image; return nullptr; } - -void SimpleAnimation::initializeAnimation(XML::Node animationNode, - const std::string& dyePalettes) -{ - if (!animationNode) - return; - - std::string imagePath = animationNode.getProperty( "imageset", ""); - - // Instanciate the dye coloration. - if (!imagePath.empty() && !dyePalettes.empty()) - Dye::instantiate(imagePath, dyePalettes); - - ImageSet *imageset = ResourceManager::getInstance()->getImageSet( - animationNode.getProperty("imageset", ""), - animationNode.getProperty("width", 0), - animationNode.getProperty("height", 0) - ); - - if (!imageset) - return; - - // Get animation frames - for (auto frameNode : animationNode.children()) - { - int delay = frameNode.getProperty("delay", 0); - int offsetX = frameNode.getProperty("offsetX", 0); - int offsetY = frameNode.getProperty("offsetY", 0); - Game *game = Game::instance(); - if (game) - { - offsetX -= imageset->getWidth() / 2 - - game->getCurrentTileWidth() / 2; - offsetY -= imageset->getHeight() - game->getCurrentTileHeight(); - } - - if (frameNode.name() == "frame") - { - int index = frameNode.getProperty("index", -1); - - if (index < 0) - { - logger->log("No valid value for 'index'"); - continue; - } - - Image *img = imageset->get(index); - - if (!img) - { - logger->log("No image at index %d", index); - continue; - } - - mAnimation.addFrame(img, delay, offsetX, offsetY); - } - else if (frameNode.name() == "sequence") - { - int start = frameNode.getProperty("start", -1); - int end = frameNode.getProperty("end", -1); - - if (start < 0 || end < 0) - { - logger->log("No valid value for 'start' or 'end'"); - continue; - } - - while (end >= start) - { - Image *img = imageset->get(start); - - if (!img) - { - logger->log("No image at index %d", start); - continue; - } - - mAnimation.addFrame(img, delay, offsetX, offsetY); - start++; - } - } - else if (frameNode.name() == "end") - { - mAnimation.addTerminator(); - } - } - - mInitialized = true; -} diff --git a/src/simpleanimation.h b/src/simpleanimation.h index 94cc3a34..299b243f 100644 --- a/src/simpleanimation.h +++ b/src/simpleanimation.h @@ -48,7 +48,7 @@ class SimpleAnimation final void setFrame(int frame); - int getLength() const; + int getLength() const { return mAnimation.getLength(); } void update(int dt); @@ -62,9 +62,6 @@ class SimpleAnimation final Image *getCurrentImage() const; private: - void initializeAnimation(XML::Node animationNode, - const std::string& dyePalettes = std::string()); - /** The hosted animation. */ Animation mAnimation; diff --git a/src/sound.cpp b/src/sound.cpp index 60cc2c10..c97951a3 100644 --- a/src/sound.cpp +++ b/src/sound.cpp @@ -30,42 +30,32 @@ #include "resources/resourcemanager.h" #include "resources/soundeffect.h" -enum { - CHANNEL_NOTIFICATIONS = 0 -}; - /** - * This will be set to true, when a music can be freed after a fade out - * Currently used by fadeOutCallBack() + * This will be set to true when the music that was playing can be freed. */ -static bool sFadingOutEnded = false; +static bool sMusicFinished; +static bool sChannelFinished[Sound::CHANNEL_COUNT]; -/** - * Callback used at end of fadeout. - * It is called by Mix_MusicFadeFinished(). - */ -static void fadeOutCallBack() +static void musicFinishedCallBack() { - sFadingOutEnded = true; + sMusicFinished = true; } -Sound::Sound(): - mInstalled(false), - mSfxVolume(100), - mNotificationsVolume(100), - mMusicVolume(60), - mMusic(nullptr) +static void channelFinishedCallBack(int channel) { - // This set up our callback function used to - // handle fade outs endings. - sFadingOutEnded = false; - Mix_HookMusicFinished(fadeOutCallBack); + sChannelFinished[channel] = true; +} + +Sound::Sound() +{ + Mix_HookMusicFinished(musicFinishedCallBack); + Mix_ChannelFinished(channelFinishedCallBack); } Sound::~Sound() { - // Unlink the callback function. Mix_HookMusicFinished(nullptr); + Mix_ChannelFinished(nullptr); } void Sound::init() @@ -93,8 +83,8 @@ void Sound::init() return; } - Mix_AllocateChannels(16); - Mix_ReserveChannels(1); // reserve one channel for notification sounds + Mix_AllocateChannels(CHANNEL_COUNT); + Mix_ReserveChannels(CHANNEL_RESERVED_COUNT); Mix_VolumeMusic(mMusicVolume); Mix_Volume(-1, mSfxVolume); Mix_Volume(CHANNEL_NOTIFICATIONS, mNotificationsVolume); @@ -173,25 +163,9 @@ void Sound::setNotificationsVolume(int volume) Mix_Volume(CHANNEL_NOTIFICATIONS, mNotificationsVolume); } -static Music *loadMusic(const std::string &fileName) -{ - ResourceManager *resman = ResourceManager::getInstance(); - return resman->getMusic(paths.getStringValue("music") + fileName); -} - - void Sound::playMusic(const std::string &fileName) { - mCurrentMusicFile = fileName; - - if (!mInstalled) - return; - - haltMusic(); - - mMusic = loadMusic(fileName); - if (mMusic) - mMusic->play(); + fadeInMusic(fileName, 0); } void Sound::stopMusic() @@ -213,7 +187,9 @@ void Sound::fadeInMusic(const std::string &fileName, int ms) haltMusic(); - mMusic = loadMusic(fileName); + ResourceManager *resman = ResourceManager::getInstance(); + mMusic = resman->getMusic(paths.getStringValue("music") + fileName); + if (mMusic) mMusic->play(-1, ms); } @@ -230,12 +206,12 @@ void Sound::fadeOutMusic(int ms) if (mMusic) { Mix_FadeOutMusic(ms); - // Note: The fadeOutCallBack handler will take care about freeing + // Note: The musicFinishedCallBack will take care about freeing // the music file at fade out ending. } else { - sFadingOutEnded = true; + sMusicFinished = true; } } @@ -247,14 +223,10 @@ void Sound::fadeOutAndPlayMusic(const std::string &fileName, int ms) void Sound::logic() { - if (sFadingOutEnded) + if (sMusicFinished) { - if (mMusic) - { - mMusic->decRef(); - mMusic = nullptr; - } - sFadingOutEnded = false; + sMusicFinished = false; + mMusic = nullptr; if (!mNextMusicFile.empty()) { @@ -262,6 +234,15 @@ void Sound::logic() mNextMusicFile.clear(); } } + + for (int i = 0; i < CHANNEL_COUNT; i++) + { + if (sChannelFinished[i]) + { + sChannelFinished[i] = false; + mSounds[i] = nullptr; + } + } } void Sound::playSfx(const std::string &path, int x, int y) @@ -277,7 +258,7 @@ void Sound::playSfx(const std::string &path, int x, int y) ResourceManager *resman = ResourceManager::getInstance(); - if (SoundEffect *sample = resman->getSoundEffect(tmpPath)) + if (ResourceRef<SoundEffect> sound = resman->getSoundEffect(tmpPath)) { logger->log("Sound::playSfx() Playing: %s", path.c_str()); int vol = 120; @@ -293,7 +274,9 @@ void Sound::playSfx(const std::string &path, int x, int y) vol -= std::min(120, dist / 4); } - sample->play(0, vol); + int channel = sound->play(0, vol); + if (channel != -1) + mSounds[channel] = sound; } } @@ -302,9 +285,11 @@ void Sound::playNotification(const std::string &path) const std::string fullPath = paths.getValue("sfx", "sfx/") + path; ResourceManager *resman = ResourceManager::getInstance(); - if (SoundEffect *sample = resman->getSoundEffect(fullPath)) + if (ResourceRef<SoundEffect> sound = resman->getSoundEffect(fullPath)) { - sample->play(0, 128, CHANNEL_NOTIFICATIONS); + int channel = sound->play(0, MIX_MAX_VOLUME, CHANNEL_NOTIFICATIONS); + if (channel != -1) + mSounds[channel] = sound; } } @@ -326,6 +311,5 @@ void Sound::haltMusic() return; Mix_HaltMusic(); - mMusic->decRef(); mMusic = nullptr; } diff --git a/src/sound.h b/src/sound.h index 9d3aa28e..98286d76 100644 --- a/src/sound.h +++ b/src/sound.h @@ -21,11 +21,14 @@ #pragma once +#include "resources/resource.h" + #include <SDL_mixer.h> #include <string> class Music; +class SoundEffect; /** Sound engine * @@ -107,11 +110,19 @@ class Sound /** * The sound logic. - * Currently used to check whether the music file can be freed after - * a fade out, and whether new music has to be played. + * + * Checks whether the music and sound effects can be freed after they + * finished playing, and whether new music has to be played. */ void logic(); + enum Channel { + CHANNEL_NOTIFICATIONS = 0, + CHANNEL_RESERVED_COUNT, + + CHANNEL_COUNT = 16, + }; + private: /** Logs various info about sound device. */ void info(); @@ -125,14 +136,15 @@ class Sound */ std::string mNextMusicFile; - bool mInstalled; + bool mInstalled = false; - int mSfxVolume; - int mNotificationsVolume; - int mMusicVolume; + int mSfxVolume = 100; + int mNotificationsVolume = 100; + int mMusicVolume = 60; std::string mCurrentMusicFile; - Music *mMusic; + ResourceRef<Music> mMusic; + ResourceRef<SoundEffect> mSounds[CHANNEL_COUNT]; }; extern Sound sound; diff --git a/src/sprite.cpp b/src/sprite.cpp index 3d9608f3..c2e434e7 100644 --- a/src/sprite.cpp +++ b/src/sprite.cpp @@ -42,12 +42,10 @@ Sprite::Sprite(SpriteDef *sprite): Sprite *Sprite::load(const std::string &filename, int variant) { ResourceManager *resman = ResourceManager::getInstance(); - SpriteDef *s = resman->getSprite(filename, variant); - if (!s) + auto spriteDef = resman->getSprite(filename, variant); + if (!spriteDef) return nullptr; - auto *as = new Sprite(s); - s->decRef(); - return as; + return new Sprite(spriteDef); } Sprite::~Sprite() = default; diff --git a/src/text.cpp b/src/text.cpp index e3776410..7f305401 100644 --- a/src/text.cpp +++ b/src/text.cpp @@ -53,7 +53,7 @@ Text::Text(const std::string &text, int x, int y, if (textManager == nullptr) { textManager = new TextManager; - Image *sbImage = Theme::getImageFromTheme("bubble.png|W:#" + auto sbImage = Theme::getImageFromTheme("bubble.png|W:#" + config.speechBubblecolor); mBubble.grid[0] = sbImage->getSubImage(0, 0, 5, 5); mBubble.grid[1] = sbImage->getSubImage(5, 0, 5, 5); @@ -67,7 +67,6 @@ Text::Text(const std::string &text, int x, int y, mBubbleArrow = sbImage->getSubImage(0, 15, 15, 10); mBubble.setAlpha(config.speechBubbleAlpha); mBubbleArrow->setAlpha(config.speechBubbleAlpha); - sbImage->decRef(); } ++mInstances; mHeight = mFont->getHeight(); diff --git a/src/utils/filesystem.h b/src/utils/filesystem.h index 10c67202..0363cd22 100644 --- a/src/utils/filesystem.h +++ b/src/utils/filesystem.h @@ -264,6 +264,15 @@ inline SDL_RWops *openRWops(const std::string &path) return PHYSFSRWOPS_openRead(path.c_str()); } +/** + * Creates a buffered SDL_RWops. + * + * Used to workaround a performance issue when SDL_mixer is using stb_vorbis. + * The overhead of calling PHYSFS_readBytes each time is too high because + * stb_vorbis requests the file one byte at a time. + * + * See https://github.com/libsdl-org/SDL_mixer/issues/670 + */ inline SDL_RWops *openBufferedRWops(const std::string &path) { auto rw = PHYSFSRWOPS_openRead(path.c_str()); |