Browse Source

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.
pull/4702/head
Gleb Mazovetskiy 4 years ago
parent
commit
9e9b656b88
  1. 17
      Source/DiabloUI/credits.cpp
  2. 43
      Source/DiabloUI/diabloui.cpp
  3. 5
      Source/DiabloUI/diabloui.h
  4. 8
      Source/DiabloUI/dialogs.cpp
  5. 7
      Source/DiabloUI/mainmenu.cpp
  6. 36
      Source/DiabloUI/progress.cpp
  7. 2
      Source/DiabloUI/selconn.cpp
  8. 2
      Source/DiabloUI/selgame.cpp
  9. 6
      Source/DiabloUI/selhero.cpp
  10. 2
      Source/DiabloUI/selok.cpp
  11. 130
      Source/DiabloUI/selstart.cpp
  12. 2
      Source/DiabloUI/selyesno.cpp
  13. 4
      Source/DiabloUI/settingsmenu.cpp
  14. 14
      Source/DiabloUI/title.cpp
  15. 54
      Source/DiabloUI/ui_item.h

17
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");
}

43
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<std::optional<OwnedCelSpriteWithFrameHeight>, 3> ArtLogos;
std::array<std::optional<OwnedCelSpriteWithFrameHeight>, 3> ArtFocus;
Art ArtBackgroundWidescreen;
Art ArtBackground;
std::optional<OwnedPcxSprite> ArtBackgroundWidescreen;
std::optional<OwnedPcxSpriteSheet> 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<uint16_t>(frames), pPal);
if (!ArtBackground)
return;
LoadPalInMem(pPal);
@ -697,13 +700,13 @@ void LoadBackgroundArt(const char *pszFile, int frames)
void UiAddBackground(std::vector<std::unique_ptr<UiItemBase>> *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<UiImage>(&ArtBackgroundWidescreen, rectw, UiFlags::AlignCenter));
vecDialog->push_back(std::make_unique<UiImagePcx>(PcxSprite { *ArtBackgroundWidescreen }, rectw, UiFlags::AlignCenter));
}
SDL_Rect rect = { 0, uiPositionY, 0, 0 };
vecDialog->push_back(std::make_unique<UiImage>(&ArtBackground, rect, UiFlags::AlignCenter));
vecDialog->push_back(std::make_unique<UiImagePcx>(PcxSpriteSheet { *ArtBackground }.sprite(0), rect, UiFlags::AlignCenter));
}
void UiAddLogo(std::vector<std::unique_ptr<UiItemBase>> *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<UiImageCel *>(item));
break;
case UiType::ImagePcx:
Render(static_cast<UiImagePcx *>(item));
break;
case UiType::ImageAnimatedPcx:
Render(static_cast<UiImageAnimatedPcx *>(item));
break;
case UiType::ArtTextButton:
Render(static_cast<UiArtTextButton *>(item));
break;

5
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<std::optional<OwnedCelSpriteWithFrameHeight>, 3> ArtLogos;
extern std::array<std::optional<OwnedCelSpriteWithFrameHeight>, 3> ArtFocus;
extern Art ArtBackground;
extern Art ArtBackgroundWidescreen;
extern std::optional<OwnedPcxSprite> ArtBackgroundWidescreen;
extern std::optional<OwnedPcxSpriteSheet> ArtBackground;
extern Art ArtCursor;
extern Art ArtHero;

8
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<std::unique_ptr<UiItemBase>> &items, const std::vector<std::unique_ptr<UiItemBase>> &renderBehind)
@ -249,7 +249,7 @@ void DialogLoop(const std::vector<std::unique_ptr<UiItemBase>> &items, const std
UiRenderItems(renderBehind);
}
UiRenderItems(items);
if (ArtBackground.surface != nullptr) {
if (ArtBackground) {
DrawMouse();
}
UiFadeIn();

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

36
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<OwnedPcxSprite> ArtPopupSm;
std::optional<OwnedPcxSprite> ArtProgBG;
std::optional<OwnedPcxSprite> ProgFil;
std::vector<std::unique_ptr<UiItemBase>> 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);
}

2
Source/DiabloUI/selconn.cpp

@ -89,7 +89,7 @@ void SelconnLoad()
void SelconnFree()
{
ArtBackground.Unload();
ArtBackground = std::nullopt;
vecConnItems.clear();

2
Source/DiabloUI/selgame.cpp

@ -60,7 +60,7 @@ void selgame_Init()
void selgame_Free()
{
ArtBackground.Unload();
ArtBackground = std::nullopt;
UnloadScrollBar();
selgame_FreeVectors();
}

6
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<HeroClass>(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 {

2
Source/DiabloUI/selok.cpp

@ -23,7 +23,7 @@ std::vector<std::unique_ptr<UiItemBase>> vecSelOkDialog;
void selok_Free()
{
ArtBackground.Unload();
ArtBackground = std::nullopt;
vecSelOkDialogItems.clear();

130
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<std::unique_ptr<UiListItem>> vecDialogItems;
std::vector<std::unique_ptr<UiItemBase>> vecDialog;
Art artLogo;
void ItemSelected(int value)
{
auto option = static_cast<StartUpGameMode>(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<UiImage>(&artLogo, rect, UiFlags::AlignCenter, /*bAnimated=*/true));
vecDialogItems.push_back(std::make_unique<UiListItem>(_("Enter Hellfire"), static_cast<int>(StartUpGameMode::Hellfire)));
vecDialogItems.push_back(std::make_unique<UiListItem>(_("Switch to Diablo"), static_cast<int>(StartUpGameMode::Diablo)));
vecDialog.push_back(std::make_unique<UiList>(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<std::unique_ptr<UiListItem>> vecDialogItems;
std::vector<std::unique_ptr<UiItemBase>> vecDialog;
Art artLogo;
void ItemSelected(int value)
{
auto option = static_cast<StartUpGameMode>(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<UiImage>(&artLogo, rect, UiFlags::AlignCenter, /*bAnimated=*/true));
vecDialogItems.push_back(std::make_unique<UiListItem>(_("Enter Hellfire"), static_cast<int>(StartUpGameMode::Hellfire)));
vecDialogItems.push_back(std::make_unique<UiListItem>(_("Switch to Diablo"), static_cast<int>(StartUpGameMode::Diablo)));
vecDialog.push_back(std::make_unique<UiList>(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

2
Source/DiabloUI/selyesno.cpp

@ -19,7 +19,7 @@ std::vector<std::unique_ptr<UiItemBase>> vecSelYesNoDialog;
void SelyesnoFree()
{
ArtBackground.Unload();
ArtBackground = std::nullopt;
vecSelYesNoDialogItems.clear();

4
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();
}

14
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<std::unique_ptr<UiItemBase>> 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<UiImage>(&ArtBackgroundWidescreen, rect, UiFlags::AlignCenter, /*bAnimated=*/true));
vecTitleScreen.push_back(std::make_unique<UiImage>(&ArtBackground, rect, UiFlags::AlignCenter, /*bAnimated=*/true));
if (ArtBackgroundWidescreen)
vecTitleScreen.push_back(std::make_unique<UiImagePcx>(PcxSprite { *ArtBackgroundWidescreen }, rect, UiFlags::AlignCenter));
vecTitleScreen.push_back(std::make_unique<UiImageAnimatedPcx>(PcxSpriteSheet { *ArtBackground }, rect, UiFlags::AlignCenter));
} else {
UiAddBackground(&vecTitleScreen);
UiAddLogo(&vecTitleScreen, LOGO_BIG, 182);

54
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 {

Loading…
Cancel
Save