diff options
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()); |