From 9e9b656b8846e494ef1a424ac5458efaa03d7754 Mon Sep 17 00:00:00 2001 From: Gleb Mazovetskiy Date: Sun, 8 May 2022 11:50:32 +0100 Subject: [PATCH] DiabloUI: Render backgrounds as PCX The backgrounds can be quite large, for example the Hellfire title background is a 2.4 MiB PCX file. Previously, we converted all background to SDL surfaces. This required extra memory, e.g. the 2.4 MiB Hellfire title background uses 4.6 MiB as an SDL surface. Changes the background to render directly from PCX instead to reduce the allocator pressure. --- Source/DiabloUI/credits.cpp | 17 ++-- Source/DiabloUI/diabloui.cpp | 43 ++++++++-- Source/DiabloUI/diabloui.h | 5 +- Source/DiabloUI/dialogs.cpp | 8 +- Source/DiabloUI/mainmenu.cpp | 7 +- Source/DiabloUI/progress.cpp | 36 +++++---- Source/DiabloUI/selconn.cpp | 2 +- Source/DiabloUI/selgame.cpp | 2 +- Source/DiabloUI/selhero.cpp | 6 +- Source/DiabloUI/selok.cpp | 2 +- Source/DiabloUI/selstart.cpp | 130 +++++++++++++++---------------- Source/DiabloUI/selyesno.cpp | 2 +- Source/DiabloUI/settingsmenu.cpp | 4 +- Source/DiabloUI/title.cpp | 14 ++-- Source/DiabloUI/ui_item.h | 54 +++++++++++++ 15 files changed, 213 insertions(+), 119 deletions(-) diff --git a/Source/DiabloUI/credits.cpp b/Source/DiabloUI/credits.cpp index 2980dd286..d906939c8 100644 --- a/Source/DiabloUI/credits.cpp +++ b/Source/DiabloUI/credits.cpp @@ -10,6 +10,8 @@ #include "control.h" #include "controls/input.h" #include "controls/menu_controls.h" +#include "engine/load_pcx.hpp" +#include "engine/render/pcx_render.hpp" #include "engine/render/text_render.hpp" #include "hwcursor.hpp" #include "utils/display.h" @@ -63,8 +65,8 @@ public: ~CreditsRenderer() { - ArtBackgroundWidescreen.Unload(); - ArtBackground.Unload(); + ArtBackgroundWidescreen = std::nullopt; + ArtBackground = std::nullopt; } void Render(); @@ -95,8 +97,9 @@ void CreditsRenderer::Render() SDL_FillRect(DiabloUiSurface(), nullptr, 0x000000); const Point uiPosition = GetUIRectangle().position; - DrawArt(uiPosition - Displacement { 320, 0 }, &ArtBackgroundWidescreen); - DrawArt(uiPosition, &ArtBackground); + if (ArtBackgroundWidescreen) + RenderPcxSprite(Surface(DiabloUiSurface()), PcxSprite { *ArtBackgroundWidescreen }, uiPosition - Displacement { 320, 0 }); + RenderPcxSprite(Surface(DiabloUiSurface()), PcxSpriteSheet { *ArtBackground }.sprite(0), uiPosition); const std::size_t linesBegin = std::max(offsetY / LINE_H, 0); const std::size_t linesEnd = std::min(linesBegin + MAX_VISIBLE_LINES, linesToRender.size()); @@ -167,7 +170,7 @@ bool TextDialog(char const *const *text, std::size_t textLines) bool UiCreditsDialog() { - LoadArt("ui_art\\creditsw.pcx", &ArtBackgroundWidescreen); + ArtBackgroundWidescreen = LoadPcxAsset("ui_art\\creditsw.pcx"); LoadBackgroundArt("ui_art\\credits.pcx"); return TextDialog(CreditLines, CreditLinesSize); @@ -176,10 +179,10 @@ bool UiCreditsDialog() bool UiSupportDialog() { if (gbIsHellfire) { - LoadArt("ui_art\\supportw.pcx", &ArtBackgroundWidescreen); + ArtBackgroundWidescreen = LoadPcxAsset("ui_art\\supportw.pcx"); LoadBackgroundArt("ui_art\\support.pcx"); } else { - LoadArt("ui_art\\creditsw.pcx", &ArtBackgroundWidescreen); + ArtBackgroundWidescreen = LoadPcxAsset("ui_art\\creditsw.pcx"); LoadBackgroundArt("ui_art\\credits.pcx"); } diff --git a/Source/DiabloUI/diabloui.cpp b/Source/DiabloUI/diabloui.cpp index 708e701fb..5f5275508 100644 --- a/Source/DiabloUI/diabloui.cpp +++ b/Source/DiabloUI/diabloui.cpp @@ -19,8 +19,11 @@ #include "discord/discord.h" #include "dx.h" #include "engine/cel_sprite.hpp" +#include "engine/load_pcx.hpp" #include "engine/load_pcx_as_cel.hpp" +#include "engine/pcx_sprite.hpp" #include "engine/render/cel_render.hpp" +#include "engine/render/pcx_render.hpp" #include "hwcursor.hpp" #include "palette.h" #include "utils/display.h" @@ -51,8 +54,8 @@ namespace devilution { std::array, 3> ArtLogos; std::array, 3> ArtFocus; -Art ArtBackgroundWidescreen; -Art ArtBackground; +std::optional ArtBackgroundWidescreen; +std::optional ArtBackground; Art ArtCursor; Art ArtHero; @@ -672,8 +675,8 @@ Sint16 GetCenterOffset(Sint16 w, Sint16 bw) void LoadBackgroundArt(const char *pszFile, int frames) { SDL_Color pPal[256]; - LoadArt(pszFile, &ArtBackground, frames, pPal); - if (ArtBackground.surface == nullptr) + ArtBackground = LoadPcxSpriteSheetAsset(pszFile, static_cast(frames), pPal); + if (!ArtBackground) return; LoadPalInMem(pPal); @@ -697,13 +700,13 @@ void LoadBackgroundArt(const char *pszFile, int frames) void UiAddBackground(std::vector> *vecDialog) { int uiPositionY = GetUIRectangle().position.y; - if (ArtBackgroundWidescreen.surface != nullptr) { + if (ArtBackgroundWidescreen) { SDL_Rect rectw = { 0, uiPositionY, 0, 0 }; - vecDialog->push_back(std::make_unique(&ArtBackgroundWidescreen, rectw, UiFlags::AlignCenter)); + vecDialog->push_back(std::make_unique(PcxSprite { *ArtBackgroundWidescreen }, rectw, UiFlags::AlignCenter)); } SDL_Rect rect = { 0, uiPositionY, 0, 0 }; - vecDialog->push_back(std::make_unique(&ArtBackground, rect, UiFlags::AlignCenter)); + vecDialog->push_back(std::make_unique(PcxSpriteSheet { *ArtBackground }.sprite(0), rect, UiFlags::AlignCenter)); } void UiAddLogo(std::vector> *vecDialog, int size, int y) @@ -840,6 +843,26 @@ void Render(const UiImageCel *uiImage) } } +void Render(const UiImagePcx *uiImage) +{ + PcxSprite sprite = uiImage->GetSprite(); + int x = uiImage->m_rect.x; + if (uiImage->IsCentered()) { + x += GetCenterOffset(sprite.width(), uiImage->m_rect.w); + } + RenderPcxSprite(Surface(DiabloUiSurface()), sprite, { x, uiImage->m_rect.y }); +} + +void Render(const UiImageAnimatedPcx *uiImage) +{ + PcxSprite sprite = uiImage->GetSprite(GetAnimationFrame(uiImage->NumFrames())); + int x = uiImage->m_rect.x; + if (uiImage->IsCentered()) { + x += GetCenterOffset(sprite.width(), uiImage->m_rect.w); + } + RenderPcxSprite(Surface(DiabloUiSurface()), sprite, { x, uiImage->m_rect.y }); +} + void Render(const UiArtTextButton *uiButton) { Rectangle rect { { uiButton->m_rect.x, uiButton->m_rect.y }, { uiButton->m_rect.w, uiButton->m_rect.h } }; @@ -925,6 +948,12 @@ void RenderItem(UiItemBase *item) case UiType::ImageCel: Render(static_cast(item)); break; + case UiType::ImagePcx: + Render(static_cast(item)); + break; + case UiType::ImageAnimatedPcx: + Render(static_cast(item)); + break; case UiType::ArtTextButton: Render(static_cast(item)); break; diff --git a/Source/DiabloUI/diabloui.h b/Source/DiabloUI/diabloui.h index 317254b02..eaa4260b8 100644 --- a/Source/DiabloUI/diabloui.h +++ b/Source/DiabloUI/diabloui.h @@ -8,6 +8,7 @@ #include "DiabloUI/art.h" #include "DiabloUI/ui_item.h" #include "engine/cel_sprite.hpp" +#include "engine/pcx_sprite.hpp" #include "player.h" #include "utils/display.h" @@ -69,8 +70,8 @@ struct _uiheroinfo { extern std::array, 3> ArtLogos; extern std::array, 3> ArtFocus; -extern Art ArtBackground; -extern Art ArtBackgroundWidescreen; +extern std::optional ArtBackgroundWidescreen; +extern std::optional ArtBackground; extern Art ArtCursor; extern Art ArtHero; diff --git a/Source/DiabloUI/dialogs.cpp b/Source/DiabloUI/dialogs.cpp index 9b39b0fbc..9017c2620 100644 --- a/Source/DiabloUI/dialogs.cpp +++ b/Source/DiabloUI/dialogs.cpp @@ -163,9 +163,9 @@ void LoadFallbackPalette() void Init(string_view caption, string_view text, bool error, bool renderBehind) { if (!renderBehind) { - ArtBackground.Unload(); + ArtBackground = std::nullopt; LoadBackgroundArt("ui_art\\black.pcx"); - if (ArtBackground.surface == nullptr) { + if (!ArtBackground) { LoadFallbackPalette(); if (SDL_ShowCursor(SDL_ENABLE) <= -1) Log("{}", SDL_GetError()); @@ -215,7 +215,7 @@ void Deinit() dialogArt.Unload(); UnloadSmlButtonArt(); vecOkDialog.clear(); - ArtBackground.Unload(); + ArtBackground = std::nullopt; } void DialogLoop(const std::vector> &items, const std::vector> &renderBehind) @@ -249,7 +249,7 @@ void DialogLoop(const std::vector> &items, const std UiRenderItems(renderBehind); } UiRenderItems(items); - if (ArtBackground.surface != nullptr) { + if (ArtBackground) { DrawMouse(); } UiFadeIn(); diff --git a/Source/DiabloUI/mainmenu.cpp b/Source/DiabloUI/mainmenu.cpp index d9149757a..f762409a8 100644 --- a/Source/DiabloUI/mainmenu.cpp +++ b/Source/DiabloUI/mainmenu.cpp @@ -2,6 +2,7 @@ #include "DiabloUI/diabloui.h" #include "DiabloUI/selok.h" #include "control.h" +#include "engine/load_pcx.hpp" #include "utils/language.h" namespace devilution { @@ -46,7 +47,7 @@ void MainmenuLoad(const char *name, void (*fnSound)(const char *file)) if (!gbIsSpawn || gbIsHellfire) { if (gbIsHellfire) - LoadArt("ui_art\\mainmenuw.pcx", &ArtBackgroundWidescreen); + ArtBackgroundWidescreen = LoadPcxAsset("ui_art\\mainmenuw.pcx"); LoadBackgroundArt("ui_art\\mainmenu.pcx"); } else { LoadBackgroundArt("ui_art\\swmmenu.pcx"); @@ -76,8 +77,8 @@ void MainmenuLoad(const char *name, void (*fnSound)(const char *file)) void MainmenuFree() { - ArtBackgroundWidescreen.Unload(); - ArtBackground.Unload(); + ArtBackgroundWidescreen = std::nullopt; + ArtBackground = std::nullopt; vecMainMenuDialog.clear(); diff --git a/Source/DiabloUI/progress.cpp b/Source/DiabloUI/progress.cpp index ba726f450..537a4e7ce 100644 --- a/Source/DiabloUI/progress.cpp +++ b/Source/DiabloUI/progress.cpp @@ -5,6 +5,9 @@ #include "controls/input.h" #include "controls/menu_controls.h" #include "dx.h" +#include "engine/load_pcx.hpp" +#include "engine/pcx_sprite.hpp" +#include "engine/render/pcx_render.hpp" #include "hwcursor.hpp" #include "palette.h" #include "utils/display.h" @@ -13,10 +16,9 @@ namespace devilution { namespace { Art dialogArt; -Art progressArt; -Art ArtPopupSm; -Art ArtProgBG; -Art ProgFil; +std::optional ArtPopupSm; +std::optional ArtProgBG; +std::optional ProgFil; std::vector> vecProgress; bool endMenu; @@ -28,9 +30,9 @@ void DialogActionCancel() void ProgressLoad() { LoadBackgroundArt("ui_art\\black.pcx"); - LoadArt("ui_art\\spopup.pcx", &ArtPopupSm); - LoadArt("ui_art\\prog_bg.pcx", &ArtProgBG); - LoadArt("ui_art\\prog_fil.pcx", &ProgFil); + ArtPopupSm = LoadPcxAsset("ui_art\\spopup.pcx"); + ArtProgBG = LoadPcxAsset("ui_art\\prog_bg.pcx"); + ProgFil = LoadPcxAsset("ui_art\\prog_fil.pcx"); LoadSmlButtonArt(); const Point uiPosition = GetUIRectangle().position; @@ -40,24 +42,28 @@ void ProgressLoad() void ProgressFree() { - ArtBackground.Unload(); - ArtPopupSm.Unload(); - ArtProgBG.Unload(); - ProgFil.Unload(); + ArtBackground = std::nullopt; + ArtPopupSm = std::nullopt; + ArtProgBG = std::nullopt; + ProgFil = std::nullopt; UnloadSmlButtonArt(); } void ProgressRender(BYTE progress) { SDL_FillRect(DiabloUiSurface(), nullptr, 0x000000); - DrawArt({ 0, 0 }, &ArtBackground); + + const Surface &out = Surface(DiabloUiSurface()); + RenderPcxSprite(out, PcxSpriteSheet { *ArtBackground }.sprite(0), { 0, 0 }); Point position = { GetCenterOffset(280), GetCenterOffset(144, gnScreenHeight) }; - DrawArt(position, &ArtPopupSm, 0, 280, 140); - DrawArt({ GetCenterOffset(227), position.y + 52 }, &ArtProgBG, 0, 227); + RenderPcxSprite(out, PcxSprite { *ArtPopupSm }, position); + RenderPcxSprite(out, PcxSprite { *ArtProgBG }, { GetCenterOffset(227), position.y + 52 }); if (progress != 0) { - DrawArt({ GetCenterOffset(227), position.y + 52 }, &ProgFil, 0, 227 * progress / 100); + const int x = GetCenterOffset(227); + const int w = 227 * progress / 100; + RenderPcxSprite(out.subregion(x, 0, w, out.h()), PcxSprite { *ProgFil }, { 0, position.y + 52 }); } DrawArt({ GetCenterOffset(110), position.y + 99 }, &SmlButton, 2, 110); } diff --git a/Source/DiabloUI/selconn.cpp b/Source/DiabloUI/selconn.cpp index 276093e24..60f3c0197 100644 --- a/Source/DiabloUI/selconn.cpp +++ b/Source/DiabloUI/selconn.cpp @@ -89,7 +89,7 @@ void SelconnLoad() void SelconnFree() { - ArtBackground.Unload(); + ArtBackground = std::nullopt; vecConnItems.clear(); diff --git a/Source/DiabloUI/selgame.cpp b/Source/DiabloUI/selgame.cpp index cff86a688..7e382d8d3 100644 --- a/Source/DiabloUI/selgame.cpp +++ b/Source/DiabloUI/selgame.cpp @@ -60,7 +60,7 @@ void selgame_Init() void selgame_Free() { - ArtBackground.Unload(); + ArtBackground = std::nullopt; UnloadScrollBar(); selgame_FreeVectors(); } diff --git a/Source/DiabloUI/selhero.cpp b/Source/DiabloUI/selhero.cpp index 4b54a510e..8a809c692 100644 --- a/Source/DiabloUI/selhero.cpp +++ b/Source/DiabloUI/selhero.cpp @@ -66,7 +66,7 @@ void SelheroUiFocusNavigationYesNo() void SelheroFree() { - ArtBackground.Unload(); + ArtBackground = std::nullopt; vecSelHeroDialog.clear(); @@ -229,7 +229,7 @@ void SelheroClassSelectorSelect(int value) { auto hClass = static_cast(vecSelHeroDlgItems[value]->m_value); if (gbIsSpawn && (hClass == HeroClass::Rogue || hClass == HeroClass::Sorcerer || (hClass == HeroClass::Bard && !hfbard_mpq))) { - ArtBackground.Unload(); + ArtBackground = std::nullopt; UiSelOkDialog(nullptr, _("The Rogue and Sorcerer are only available in the full retail version of Diablo. Visit https://www.gog.com/game/diablo to purchase.").c_str(), false); LoadBackgroundArt("ui_art\\selhero.pcx"); SelheroListSelect(selhero_SaveCount); @@ -275,7 +275,7 @@ void SelheroNameSelect(int /*value*/) { // only check names in multiplayer, we don't care about them in single if (selhero_isMultiPlayer && !UiValidPlayerName(selhero_heroInfo.name)) { - ArtBackground.Unload(); + ArtBackground = std::nullopt; UiSelOkDialog(title, _("Invalid name. A name cannot contain spaces, reserved characters, or reserved words.\n").c_str(), false); LoadBackgroundArt("ui_art\\selhero.pcx"); } else { diff --git a/Source/DiabloUI/selok.cpp b/Source/DiabloUI/selok.cpp index 364a52282..cf3becc1c 100644 --- a/Source/DiabloUI/selok.cpp +++ b/Source/DiabloUI/selok.cpp @@ -23,7 +23,7 @@ std::vector> vecSelOkDialog; void selok_Free() { - ArtBackground.Unload(); + ArtBackground = std::nullopt; vecSelOkDialogItems.clear(); diff --git a/Source/DiabloUI/selstart.cpp b/Source/DiabloUI/selstart.cpp index 3cb94cb7a..d28f1a97e 100644 --- a/Source/DiabloUI/selstart.cpp +++ b/Source/DiabloUI/selstart.cpp @@ -1,65 +1,65 @@ -#include "selstart.h" - -#include "DiabloUI/diabloui.h" -#include "control.h" -#include "options.h" -#include "utils/language.h" - -namespace devilution { -namespace { - -bool endMenu; - -std::vector> vecDialogItems; -std::vector> vecDialog; - -Art artLogo; - -void ItemSelected(int value) -{ - auto option = static_cast(vecDialogItems[value]->m_value); - sgOptions.StartUp.gameMode.SetValue(option); - SaveOptions(); - endMenu = true; -} - -void EscPressed() -{ - endMenu = true; -} - -} // namespace - -void UiSelStartUpGameOption() -{ - LoadArt("ui_art\\mainmenuw.pcx", &ArtBackgroundWidescreen); - LoadBackgroundArt("ui_art\\mainmenu.pcx"); - LoadMaskedArt("ui_art\\hf_logo2.pcx", &artLogo, 16); - UiAddBackground(&vecDialog); - - const Point uiPosition = GetUIRectangle().position; - - SDL_Rect rect = { 0, (Sint16)(uiPosition.y), 0, 0 }; - vecDialog.push_back(std::make_unique(&artLogo, rect, UiFlags::AlignCenter, /*bAnimated=*/true)); - - vecDialogItems.push_back(std::make_unique(_("Enter Hellfire"), static_cast(StartUpGameMode::Hellfire))); - vecDialogItems.push_back(std::make_unique(_("Switch to Diablo"), static_cast(StartUpGameMode::Diablo))); - vecDialog.push_back(std::make_unique(vecDialogItems, vecDialogItems.size(), uiPosition.x + 64, (uiPosition.y + 240), 510, 43, UiFlags::AlignCenter | UiFlags::FontSize42 | UiFlags::ColorUiGold, 5)); - - UiInitList(nullptr, ItemSelected, EscPressed, vecDialog, true); - - endMenu = false; - while (!endMenu) { - UiClearScreen(); - UiRenderItems(vecDialog); - UiPollAndRender(); - } - - artLogo.Unload(); - ArtBackground.Unload(); - ArtBackgroundWidescreen.Unload(); - vecDialogItems.clear(); - vecDialog.clear(); -} - -} // namespace devilution +#include "selstart.h" + +#include "DiabloUI/diabloui.h" +#include "control.h" +#include "engine/load_pcx.hpp" +#include "options.h" +#include "utils/language.h" + +namespace devilution { +namespace { + +bool endMenu; + +std::vector> vecDialogItems; +std::vector> vecDialog; + +Art artLogo; + +void ItemSelected(int value) +{ + auto option = static_cast(vecDialogItems[value]->m_value); + sgOptions.StartUp.gameMode.SetValue(option); + SaveOptions(); + endMenu = true; +} + +void EscPressed() +{ + endMenu = true; +} + +} // namespace + +void UiSelStartUpGameOption() +{ + ArtBackgroundWidescreen = LoadPcxAsset("ui_art\\mainmenuw.pcx"); + LoadBackgroundArt("ui_art\\mainmenu.pcx"); + LoadMaskedArt("ui_art\\hf_logo2.pcx", &artLogo, 16); + UiAddBackground(&vecDialog); + + const Point uiPosition = GetUIRectangle().position; + SDL_Rect rect = { 0, (Sint16)(uiPosition.y), 0, 0 }; + vecDialog.push_back(std::make_unique(&artLogo, rect, UiFlags::AlignCenter, /*bAnimated=*/true)); + + vecDialogItems.push_back(std::make_unique(_("Enter Hellfire"), static_cast(StartUpGameMode::Hellfire))); + vecDialogItems.push_back(std::make_unique(_("Switch to Diablo"), static_cast(StartUpGameMode::Diablo))); + vecDialog.push_back(std::make_unique(vecDialogItems, vecDialogItems.size(), uiPosition.x + 64, uiPosition.y + 240, 510, 43, UiFlags::AlignCenter | UiFlags::FontSize42 | UiFlags::ColorUiGold, 5)); + + UiInitList(nullptr, ItemSelected, EscPressed, vecDialog, true); + + endMenu = false; + while (!endMenu) { + UiClearScreen(); + UiRenderItems(vecDialog); + UiPollAndRender(); + } + + artLogo.Unload(); + ArtBackground = std::nullopt; + ArtBackgroundWidescreen = std::nullopt; + vecDialogItems.clear(); + vecDialog.clear(); +} + +} // namespace devilution diff --git a/Source/DiabloUI/selyesno.cpp b/Source/DiabloUI/selyesno.cpp index d458ff2f8..0b33c7562 100644 --- a/Source/DiabloUI/selyesno.cpp +++ b/Source/DiabloUI/selyesno.cpp @@ -19,7 +19,7 @@ std::vector> vecSelYesNoDialog; void SelyesnoFree() { - ArtBackground.Unload(); + ArtBackground = std::nullopt; vecSelYesNoDialogItems.clear(); diff --git a/Source/DiabloUI/settingsmenu.cpp b/Source/DiabloUI/settingsmenu.cpp index f17a698ef..ac42578ea 100644 --- a/Source/DiabloUI/settingsmenu.cpp +++ b/Source/DiabloUI/settingsmenu.cpp @@ -73,8 +73,8 @@ void CleanUpSettingsUI() vecDialog.clear(); vecOptions.clear(); - ArtBackground.Unload(); - ArtBackgroundWidescreen.Unload(); + ArtBackground = std::nullopt; + ArtBackgroundWidescreen = std::nullopt; UnloadScrollBar(); } diff --git a/Source/DiabloUI/title.cpp b/Source/DiabloUI/title.cpp index eab711f85..41e683883 100644 --- a/Source/DiabloUI/title.cpp +++ b/Source/DiabloUI/title.cpp @@ -3,6 +3,7 @@ #include "controls/input.h" #include "controls/menu_controls.h" #include "discord/discord.h" +#include "engine/load_pcx.hpp" #include "engine/load_pcx_as_cel.hpp" #include "utils/language.h" @@ -14,10 +15,8 @@ std::vector> vecTitleScreen; void TitleLoad() { if (gbIsHellfire) { - // This is a 2.4 MiB PCX file without transparency (4.6 MiB as an SDL surface). LoadBackgroundArt("ui_art\\hf_logo1.pcx", 16); - - LoadArt("ui_art\\hf_titlew.pcx", &ArtBackgroundWidescreen); + ArtBackgroundWidescreen = LoadPcxAsset("ui_art\\hf_titlew.pcx"); } else { LoadBackgroundArt("ui_art\\title.pcx"); ArtLogos[LOGO_BIG] = LoadPcxAssetAsCel("ui_art\\logo.pcx", /*numFrames=*/15, /*generateFrameHeaders=*/false, /*transparentColorIndex=*/250); @@ -26,8 +25,8 @@ void TitleLoad() void TitleFree() { - ArtBackground.Unload(); - ArtBackgroundWidescreen.Unload(); + ArtBackground = std::nullopt; + ArtBackgroundWidescreen = std::nullopt; ArtLogos[LOGO_BIG] = std::nullopt; vecTitleScreen.clear(); @@ -41,8 +40,9 @@ void UiTitleDialog() const Point uiPosition = GetUIRectangle().position; if (gbIsHellfire) { SDL_Rect rect = { 0, uiPosition.y, 0, 0 }; - vecTitleScreen.push_back(std::make_unique(&ArtBackgroundWidescreen, rect, UiFlags::AlignCenter, /*bAnimated=*/true)); - vecTitleScreen.push_back(std::make_unique(&ArtBackground, rect, UiFlags::AlignCenter, /*bAnimated=*/true)); + if (ArtBackgroundWidescreen) + vecTitleScreen.push_back(std::make_unique(PcxSprite { *ArtBackgroundWidescreen }, rect, UiFlags::AlignCenter)); + vecTitleScreen.push_back(std::make_unique(PcxSpriteSheet { *ArtBackground }, rect, UiFlags::AlignCenter)); } else { UiAddBackground(&vecTitleScreen); UiAddLogo(&vecTitleScreen, LOGO_BIG, 182); diff --git a/Source/DiabloUI/ui_item.h b/Source/DiabloUI/ui_item.h index ccdb248b4..6748b75ad 100644 --- a/Source/DiabloUI/ui_item.h +++ b/Source/DiabloUI/ui_item.h @@ -8,6 +8,7 @@ #include "DiabloUI/art.h" #include "DiabloUI/ui_flags.hpp" #include "engine/cel_sprite.hpp" +#include "engine/pcx_sprite.hpp" #include "engine/render/text_render.hpp" #include "utils/enum_traits.h" #include "utils/stubs.h" @@ -20,6 +21,8 @@ enum class UiType { ArtTextButton, Image, ImageCel, + ImagePcx, + ImageAnimatedPcx, Button, List, Scrollbar, @@ -173,6 +176,57 @@ private: int frame_; }; +//============================================================================= +class UiImagePcx : public UiItemBase { +public: + UiImagePcx(PcxSprite sprite, SDL_Rect rect, UiFlags flags = UiFlags::None) + : UiItemBase(UiType::ImagePcx, rect, flags) + , sprite_(sprite) + { + } + + [[nodiscard]] bool IsCentered() const + { + return HasAnyOf(GetFlags(), UiFlags::AlignCenter); + } + + [[nodiscard]] PcxSprite GetSprite() const + { + return sprite_; + } + +private: + PcxSprite sprite_; +}; + +//============================================================================= +class UiImageAnimatedPcx : public UiItemBase { +public: + UiImageAnimatedPcx(PcxSpriteSheet sheet, SDL_Rect rect, UiFlags flags = UiFlags::None) + : UiItemBase(UiType::ImageAnimatedPcx, rect, flags) + , sheet_(sheet) + { + } + + [[nodiscard]] bool IsCentered() const + { + return HasAnyOf(GetFlags(), UiFlags::AlignCenter); + } + + [[nodiscard]] PcxSprite GetSprite(uint16_t frame) const + { + return sheet_.sprite(frame); + } + + [[nodiscard]] uint16_t NumFrames() const + { + return sheet_.numFrames(); + } + +private: + PcxSpriteSheet sheet_; +}; + //============================================================================= class UiArtText : public UiItemBase {