diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index cef9d50cc..1583510f1 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -153,6 +153,7 @@ set(libdevilutionx_SRCS storm/storm_net.cpp storm/storm_svid.cpp + utils/cel_to_cl2.cpp utils/console.cpp utils/display.cpp utils/file_util.cpp diff --git a/Source/DiabloUI/diabloui.cpp b/Source/DiabloUI/diabloui.cpp index 2681e2550..8a4f6a840 100644 --- a/Source/DiabloUI/diabloui.cpp +++ b/Source/DiabloUI/diabloui.cpp @@ -17,7 +17,7 @@ #include "engine/dx.h" #include "engine/load_pcx.hpp" #include "engine/pcx_sprite.hpp" -#include "engine/render/cel_render.hpp" +#include "engine/render/cl2_render.hpp" #include "engine/render/pcx_render.hpp" #include "hwcursor.hpp" #include "utils/display.h" @@ -748,14 +748,14 @@ void UiFadeIn() void DrawCel(CelSpriteWithFrameHeight sprite, Point p) { const Surface &out = Surface(DiabloUiSurface()); - CelDrawTo(out, { p.x, static_cast(p.y + sprite.frameHeight) }, sprite.sprite, 0); + Cl2Draw(out, { p.x, static_cast(p.y + sprite.frameHeight) }, sprite.sprite, 0); } void DrawAnimatedCel(CelSpriteWithFrameHeight sprite, Point p) { const Surface &out = Surface(DiabloUiSurface()); const int frame = GetAnimationFrame(LoadLE32(sprite.sprite.Data())); - CelDrawTo(out, { p.x, static_cast(p.y + sprite.frameHeight) }, sprite.sprite, frame); + Cl2Draw(out, { p.x, static_cast(p.y + sprite.frameHeight) }, sprite.sprite, frame); } void DrawSelector(const SDL_Rect &rect) diff --git a/Source/control.cpp b/Source/control.cpp index 6aa458a41..068b819b4 100644 --- a/Source/control.cpp +++ b/Source/control.cpp @@ -20,7 +20,7 @@ #include "cursor.h" #include "engine/cel_sprite.hpp" #include "engine/load_cel.hpp" -#include "engine/render/cel_render.hpp" +#include "engine/render/cl2_render.hpp" #include "engine/render/text_render.hpp" #include "engine/trn.hpp" #include "error.h" @@ -316,7 +316,7 @@ int DrawDurIcon4Item(const Surface &out, Item &pItem, int x, int c) } if (pItem._iDurability > 2) c += 8; - CelDrawTo(out, { x, -17 + GetMainPanel().position.y }, CelSprite { *pDurIcons }, c); + Cl2Draw(out, { x, -17 + GetMainPanel().position.y }, CelSprite { *pDurIcons }, c); return x - 32 - 8; } @@ -552,25 +552,25 @@ void InitControlPan() LoadCharPanel(); LoadSpellIcons(); { - const OwnedCelSprite sprite = LoadCel("CtrlPan\\Panel8.CEL", GetMainPanel().size.width); - CelDrawUnsafeTo(*pBtmBuff, { 0, (GetMainPanel().size.height + 16) - 1 }, CelSprite { sprite }, 0); + const OwnedCelSprite sprite = LoadCelAsCl2("CtrlPan\\Panel8.CEL", GetMainPanel().size.width); + Cl2Draw(*pBtmBuff, { 0, (GetMainPanel().size.height + 16) - 1 }, CelSprite { sprite }, 0); } { const Point bulbsPosition { 0, 87 }; - const OwnedCelSprite statusPanel = LoadCel("CtrlPan\\P8Bulbs.CEL", 88); - CelDrawUnsafeTo(*pLifeBuff, bulbsPosition, CelSprite { statusPanel }, 0); - CelDrawUnsafeTo(*pManaBuff, bulbsPosition, CelSprite { statusPanel }, 1); + const OwnedCelSprite statusPanel = LoadCelAsCl2("CtrlPan\\P8Bulbs.CEL", 88); + Cl2Draw(*pLifeBuff, bulbsPosition, CelSprite { statusPanel }, 0); + Cl2Draw(*pManaBuff, bulbsPosition, CelSprite { statusPanel }, 1); } } talkflag = false; if (IsChatAvailable()) { if (!HeadlessMode) { { - const OwnedCelSprite sprite = LoadCel("CtrlPan\\TalkPanl.CEL", GetMainPanel().size.width); - CelDrawUnsafeTo(*pBtmBuff, { 0, (GetMainPanel().size.height + 16) * 2 - 1 }, CelSprite { sprite }, 0); + const OwnedCelSprite sprite = LoadCelAsCl2("CtrlPan\\TalkPanl.CEL", GetMainPanel().size.width); + Cl2Draw(*pBtmBuff, { 0, (GetMainPanel().size.height + 16) * 2 - 1 }, CelSprite { sprite }, 0); } - multiButtons = LoadCel("CtrlPan\\P8But2.CEL", 33); - talkButtons = LoadCel("CtrlPan\\TalkButt.CEL", 61); + multiButtons = LoadCelAsCl2("CtrlPan\\P8But2.CEL", 33); + talkButtons = LoadCelAsCl2("CtrlPan\\TalkButt.CEL", 61); } sgbPlrTalkTbl = 0; TalkMessage[0] = '\0'; @@ -583,8 +583,10 @@ void InitControlPan() lvlbtndown = false; if (!HeadlessMode) { LoadMainPanel(); - pPanelButtons = LoadCel("CtrlPan\\Panel8bu.CEL", 71); - pChrButtons = LoadCel("Data\\CharBut.CEL", 41); + pPanelButtons = LoadCelAsCl2("CtrlPan\\Panel8bu.CEL", 71); + + static const uint16_t CharButtonsFrameWidths[9] { 95, 41, 41, 41, 41, 41, 41, 41, 41 }; + pChrButtons = LoadCelAsCl2("Data\\CharBut.CEL", CharButtonsFrameWidths); } ClearPanBtn(); if (!IsChatAvailable()) @@ -592,7 +594,7 @@ void InitControlPan() else PanelButtonIndex = 8; if (!HeadlessMode) - pDurIcons = LoadCel("Items\\DurIcons.CEL", 32); + pDurIcons = LoadCelAsCl2("Items\\DurIcons.CEL", 32); for (bool &buttonEnabled : chrbtn) buttonEnabled = false; chrbtnactive = false; @@ -607,8 +609,8 @@ void InitControlPan() if (!HeadlessMode) { InitSpellBook(); - pQLogCel = LoadCel("Data\\Quest.CEL", static_cast(SidePanelSize.width)); - pGBoxBuff = LoadCel("CtrlPan\\Golddrop.cel", 261); + pQLogCel = LoadCelAsCl2("Data\\Quest.CEL", static_cast(SidePanelSize.width)); + pGBoxBuff = LoadCelAsCl2("CtrlPan\\Golddrop.cel", 261); } CloseGoldDrop(); dropGoldValue = 0; @@ -635,17 +637,17 @@ void DrawCtrlBtns(const Surface &out) DrawPanelBox(out, MakeSdlRect(PanBtnPos[i].x, PanBtnPos[i].y + 16, 71, 20), mainPanelPosition + Displacement { PanBtnPos[i].x, PanBtnPos[i].y }); } else { Point position = mainPanelPosition + Displacement { PanBtnPos[i].x, PanBtnPos[i].y + 18 }; - CelDrawTo(out, position, CelSprite { *pPanelButtons }, i); + Cl2Draw(out, position, CelSprite { *pPanelButtons }, i); DrawArt(out, position + Displacement { 4, -18 }, &PanelButtonDown, i); } } if (PanelButtonIndex == 8) { CelSprite sprite { *multiButtons }; - CelDrawTo(out, mainPanelPosition + Displacement { 87, 122 }, sprite, PanelButtons[6] ? 1 : 0); + Cl2Draw(out, mainPanelPosition + Displacement { 87, 122 }, sprite, PanelButtons[6] ? 1 : 0); if (MyPlayer->friendlyMode) - CelDrawTo(out, mainPanelPosition + Displacement { 527, 122 }, sprite, PanelButtons[7] ? 3 : 2); + Cl2Draw(out, mainPanelPosition + Displacement { 527, 122 }, sprite, PanelButtons[7] ? 3 : 2); else - CelDrawTo(out, mainPanelPosition + Displacement { 527, 122 }, sprite, PanelButtons[7] ? 5 : 4); + Cl2Draw(out, mainPanelPosition + Displacement { 527, 122 }, sprite, PanelButtons[7] ? 5 : 4); } } @@ -970,7 +972,7 @@ void DrawLevelUpIcon(const Surface &out) if (IsLevelUpButtonVisible()) { int nCel = lvlbtndown ? 2 : 1; DrawString(out, _("Level Up"), { GetMainPanel().position + Displacement { 0, -62 }, { 120, 0 } }, UiFlags::ColorWhite | UiFlags::AlignCenter); - CelDrawTo(out, GetMainPanel().position + Displacement { 40, -17 }, CelSprite { *pChrButtons }, nCel); + Cl2Draw(out, GetMainPanel().position + Displacement { 40, -17 }, CelSprite { *pChrButtons }, nCel); } } @@ -1072,7 +1074,7 @@ void DrawGoldSplit(const Surface &out, int amount) { const int dialogX = 30; - CelDrawTo(out, GetPanelPosition(UiPanels::Inventory, { dialogX, 178 }), CelSprite { *pGBoxBuff }, 0); + Cl2Draw(out, GetPanelPosition(UiPanels::Inventory, { dialogX, 178 }), CelSprite { *pGBoxBuff }, 0); const std::string description = fmt::format( fmt::runtime(ngettext( @@ -1161,14 +1163,14 @@ void DrawTalkPan(const Surface &out) if (WhisperList[i]) { if (TalkButtonsDown[talkBtn]) { int nCel = talkBtn != 0 ? 3 : 2; - CelDrawTo(out, talkPanPosition, CelSprite { *talkButtons }, nCel); + Cl2Draw(out, talkPanPosition, CelSprite { *talkButtons }, nCel); DrawArt(out, talkPanPosition + Displacement { 4, -15 }, &TalkButton, 2); } } else { int nCel = talkBtn != 0 ? 1 : 0; if (TalkButtonsDown[talkBtn]) nCel += 4; - CelDrawTo(out, talkPanPosition, CelSprite { *talkButtons }, nCel); + Cl2Draw(out, talkPanPosition, CelSprite { *talkButtons }, nCel); DrawArt(out, talkPanPosition + Displacement { 4, -15 }, &TalkButton, TalkButtonsDown[talkBtn] ? 1 : 0); } if (player.plractive) { diff --git a/Source/controls/touch/renderers.cpp b/Source/controls/touch/renderers.cpp index cebe9e211..81ed2c15c 100644 --- a/Source/controls/touch/renderers.cpp +++ b/Source/controls/touch/renderers.cpp @@ -5,7 +5,7 @@ #include "diablo.h" #include "doom.h" #include "engine.h" -#include "engine/render/cel_render.hpp" +#include "engine/render/cl2_render.hpp" #include "init.h" #include "inv.h" #include "levels/gendung.h" @@ -143,7 +143,7 @@ void LoadPotionArt(Art *potionArt, SDL_Renderer *renderer) const int frame = GetInvItemFrame(cursorID); const CelSprite potionSprite { GetInvItemSprite(cursorID) }; position.y += potionSize.height; - CelClippedDrawTo(Surface(surface.get()), position, potionSprite, frame); + Cl2Draw(Surface(surface.get()), position, potionSprite, frame); } potionArt->logical_width = potionSize.width; diff --git a/Source/cursor.cpp b/Source/cursor.cpp index 853c3e5e3..9786b9008 100644 --- a/Source/cursor.cpp +++ b/Source/cursor.cpp @@ -15,7 +15,8 @@ #include "engine.h" #include "engine/load_cel.hpp" #include "engine/point.hpp" -#include "engine/render/cel_render.hpp" +#include "engine/render/cl2_render.hpp" +#include "engine/trn.hpp" #include "hwcursor.hpp" #include "inv.h" #include "levels/trigs.h" @@ -65,7 +66,8 @@ const uint16_t InvItemWidth2[] = { 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 2 * 28, 2 * 28, 1 * 28, 1 * 28, 1 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, - 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28 + 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, + 2 * 28 // clang-format on }; constexpr uint16_t InvItems1Size = sizeof(InvItemWidth1) / sizeof(InvItemWidth1[0]); @@ -101,7 +103,8 @@ const uint16_t InvItemHeight2[InvItems2Size] = { 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 2 * 28, 2 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, - 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28 + 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, + 3 * 28 // clang-format on }; @@ -130,9 +133,9 @@ int pcurs; void InitCursor() { assert(!pCursCels); - pCursCels = LoadCel("Data\\Inv\\Objcurs.CEL", InvItemWidth1); + pCursCels = LoadCelAsCl2("Data\\Inv\\Objcurs.CEL", InvItemWidth1); if (gbIsHellfire) - pCursCels2 = LoadCel("Data\\Inv\\Objcurs2.CEL", InvItemWidth2); + pCursCels2 = LoadCelAsCl2("Data\\Inv\\Objcurs2.CEL", InvItemWidth2); ClearCursor(); } @@ -161,6 +164,16 @@ Size GetInvItemSize(int cursId) return { InvItemWidth1[i], InvItemHeight1[i] }; } +void DrawItem(const Item &item, const Surface &out, Point position, CelSprite cel, int frame) +{ + const bool usable = item._iStatFlag; + if (usable) { + Cl2Draw(out, position, cel, frame); + } else { + Cl2DrawTRN(out, position, cel, frame, GetInfravisionTRN()); + } +} + void ResetCursor() { NewCursor(pcurs); @@ -194,16 +207,16 @@ void NewCursor(int cursId) } } -void CelDrawCursor(const Surface &out, Point position, int cursId) +void DrawSoftwareCursor(const Surface &out, Point position, int cursId) { const CelSprite sprite { GetInvItemSprite(cursId) }; const int frame = GetInvItemFrame(cursId); if (!MyPlayer->HoldItem.isEmpty()) { const auto &heldItem = MyPlayer->HoldItem; - CelBlitOutlineTo(out, GetOutlineColor(heldItem, true), position, sprite, frame, false); - CelDrawItem(heldItem, out, position, sprite, frame); + Cl2DrawOutline(out, GetOutlineColor(heldItem, true), position, sprite, frame); + DrawItem(heldItem, out, position, sprite, frame); } else { - CelClippedDrawTo(out, position, sprite, frame); + Cl2Draw(out, position, sprite, frame); } } diff --git a/Source/cursor.h b/Source/cursor.h index ced3b06b1..e70f5cbe8 100644 --- a/Source/cursor.h +++ b/Source/cursor.h @@ -57,7 +57,9 @@ void CheckRportal(); void CheckTown(); void CheckCursMove(); -void CelDrawCursor(const Surface &out, Point position, int cursId); +void DrawSoftwareCursor(const Surface &out, Point position, int cursId); + +void DrawItem(const Item &item, const Surface &out, Point position, CelSprite cel, int frame); /** Returns the sprite for the given inventory index. */ const OwnedCelSprite &GetInvItemSprite(int i); diff --git a/Source/debug.cpp b/Source/debug.cpp index b5db16209..6bb6715e2 100644 --- a/Source/debug.cpp +++ b/Source/debug.cpp @@ -990,7 +990,7 @@ std::vector DebugCmdList = { void LoadDebugGFX() { - pSquareCel = LoadCel("Data\\Square.CEL", 64); + pSquareCel = LoadCelAsCl2("Data\\Square.CEL", 64); } void FreeDebugGFX() diff --git a/Source/diablo.cpp b/Source/diablo.cpp index b78d65cec..b2e8a4fed 100644 --- a/Source/diablo.cpp +++ b/Source/diablo.cpp @@ -1090,37 +1090,37 @@ void LoadLvlGFX() pDungeonCels = LoadFileInMem("Levels\\TownData\\Town.CEL"); pMegaTiles = LoadFileInMem("Levels\\TownData\\Town.TIL"); } - pSpecialCels = LoadCel("Levels\\TownData\\TownS.CEL", SpecialCelWidth); + pSpecialCels = LoadCelAsCl2("Levels\\TownData\\TownS.CEL", SpecialCelWidth); break; case DTYPE_CATHEDRAL: pDungeonCels = LoadFileInMem("Levels\\L1Data\\L1.CEL"); pMegaTiles = LoadFileInMem("Levels\\L1Data\\L1.TIL"); - pSpecialCels = LoadCel("Levels\\L1Data\\L1S.CEL", SpecialCelWidth); + pSpecialCels = LoadCelAsCl2("Levels\\L1Data\\L1S.CEL", SpecialCelWidth); break; case DTYPE_CATACOMBS: pDungeonCels = LoadFileInMem("Levels\\L2Data\\L2.CEL"); pMegaTiles = LoadFileInMem("Levels\\L2Data\\L2.TIL"); - pSpecialCels = LoadCel("Levels\\L2Data\\L2S.CEL", SpecialCelWidth); + pSpecialCels = LoadCelAsCl2("Levels\\L2Data\\L2S.CEL", SpecialCelWidth); break; case DTYPE_CAVES: pDungeonCels = LoadFileInMem("Levels\\L3Data\\L3.CEL"); pMegaTiles = LoadFileInMem("Levels\\L3Data\\L3.TIL"); - pSpecialCels = LoadCel("Levels\\L1Data\\L1S.CEL", SpecialCelWidth); + pSpecialCels = LoadCelAsCl2("Levels\\L1Data\\L1S.CEL", SpecialCelWidth); break; case DTYPE_HELL: pDungeonCels = LoadFileInMem("Levels\\L4Data\\L4.CEL"); pMegaTiles = LoadFileInMem("Levels\\L4Data\\L4.TIL"); - pSpecialCels = LoadCel("Levels\\L2Data\\L2S.CEL", SpecialCelWidth); + pSpecialCels = LoadCelAsCl2("Levels\\L2Data\\L2S.CEL", SpecialCelWidth); break; case DTYPE_NEST: pDungeonCels = LoadFileInMem("NLevels\\L6Data\\L6.CEL"); pMegaTiles = LoadFileInMem("NLevels\\L6Data\\L6.TIL"); - pSpecialCels = LoadCel("Levels\\L1Data\\L1S.CEL", SpecialCelWidth); + pSpecialCels = LoadCelAsCl2("Levels\\L1Data\\L1S.CEL", SpecialCelWidth); break; case DTYPE_CRYPT: pDungeonCels = LoadFileInMem("NLevels\\L5Data\\L5.CEL"); pMegaTiles = LoadFileInMem("NLevels\\L5Data\\L5.TIL"); - pSpecialCels = LoadCel("NLevels\\L5Data\\L5S.CEL", SpecialCelWidth); + pSpecialCels = LoadCelAsCl2("NLevels\\L5Data\\L5S.CEL", SpecialCelWidth); break; default: app_fatal("LoadLvlGFX"); diff --git a/Source/doom.cpp b/Source/doom.cpp index ab35298ce..626e46160 100644 --- a/Source/doom.cpp +++ b/Source/doom.cpp @@ -9,7 +9,7 @@ #include "engine.h" #include "engine/cel_sprite.hpp" #include "engine/load_cel.hpp" -#include "engine/render/cel_render.hpp" +#include "engine/render/cl2_render.hpp" #include "utils/stdcompat/optional.hpp" namespace devilution { @@ -21,7 +21,7 @@ bool DoomFlag; void doom_init() { - DoomCel = LoadCel("Items\\Map\\MapZtown.CEL", 640); + DoomCel = LoadCelAsCl2("Items\\Map\\MapZtown.CEL", 640); DoomFlag = true; } @@ -37,7 +37,7 @@ void doom_draw(const Surface &out) return; } - CelDrawTo(out, GetUIRectangle().position + Displacement { 0, 352 }, CelSprite { *DoomCel }, 0); + Cl2Draw(out, GetUIRectangle().position + Displacement { 0, 352 }, CelSprite { *DoomCel }, 0); } } // namespace devilution diff --git a/Source/engine/cel_sprite.hpp b/Source/engine/cel_sprite.hpp index 793cb8d92..11c74d36f 100644 --- a/Source/engine/cel_sprite.hpp +++ b/Source/engine/cel_sprite.hpp @@ -54,6 +54,11 @@ public: return width_.HoldsPointer() ? width_.AsPointer()[frame] : width_.AsValue(); } + [[nodiscard]] PointerOrValue widthOrWidths() const + { + return width_; + } + [[nodiscard]] bool operator==(CelSprite other) const { return data_ptr_ == other.data_ptr_; @@ -162,6 +167,12 @@ public: { } + OwnedCelSprite(std::unique_ptr data, PointerOrValue widths) + : data_(std::move(data)) + , width_(widths) + { + } + OwnedCelSprite(OwnedCelSprite &&) noexcept = default; OwnedCelSprite &operator=(OwnedCelSprite &&) noexcept = default; @@ -170,6 +181,11 @@ public: return data_.get(); } + std::unique_ptr data() && + { + return std::move(data_); + } + private: // for OptionalOwnedCelSprite. OwnedCelSprite() diff --git a/Source/engine/load_cel.cpp b/Source/engine/load_cel.cpp index 14261f5e5..7a1de7cb6 100644 --- a/Source/engine/load_cel.cpp +++ b/Source/engine/load_cel.cpp @@ -1,6 +1,12 @@ #include "engine/load_cel.hpp" +#ifdef DEBUG_CEL_TO_CL2_SIZE +#include +#endif + #include "engine/load_file.hpp" +#include "utils/cel_to_cl2.hpp" +#include "utils/pointer_value_union.hpp" namespace devilution { @@ -14,4 +20,24 @@ OwnedCelSprite LoadCel(const char *pszName, const uint16_t *widths) return OwnedCelSprite(LoadFileInMem(pszName), widths); } +OwnedCelSprite LoadCelAsCl2(const char *pszName, uint16_t width) +{ + size_t size; + std::unique_ptr data = LoadFileInMem(pszName, &size); +#ifdef DEBUG_CEL_TO_CL2_SIZE + std::cout << pszName; +#endif + return CelToCl2(data.get(), size, PointerOrValue { width }); +} + +OwnedCelSprite LoadCelAsCl2(const char *pszName, const uint16_t *widths) +{ + size_t size; + std::unique_ptr data = LoadFileInMem(pszName, &size); +#ifdef DEBUG_CEL_TO_CL2_SIZE + std::cout << pszName; +#endif + return CelToCl2(data.get(), size, PointerOrValue { widths }); +} + } // namespace devilution diff --git a/Source/engine/load_cel.hpp b/Source/engine/load_cel.hpp index 14e031750..5e7f9e77a 100644 --- a/Source/engine/load_cel.hpp +++ b/Source/engine/load_cel.hpp @@ -12,4 +12,7 @@ namespace devilution { OwnedCelSprite LoadCel(const char *pszName, uint16_t width); OwnedCelSprite LoadCel(const char *pszName, const uint16_t *widths); +OwnedCelSprite LoadCelAsCl2(const char *pszName, uint16_t width); +OwnedCelSprite LoadCelAsCl2(const char *pszName, const uint16_t *widths); + } // namespace devilution diff --git a/Source/engine/render/cel_render.hpp b/Source/engine/render/cel_render.hpp index b2443d5ab..a90934021 100644 --- a/Source/engine/render/cel_render.hpp +++ b/Source/engine/render/cel_render.hpp @@ -111,6 +111,6 @@ void CelDrawItem(const Item &item, const Surface &out, Point position, CelSprite * @param frame CEL frame number * @param skipColorIndexZero If true, color in index 0 will be treated as transparent (these are typically used for shadows in sprites) */ -void CelBlitOutlineTo(const Surface &out, uint8_t col, Point position, CelSprite cel, int frame, bool skipColorIndexZero = true); +void Cl2DrawOutline(const Surface &out, uint8_t col, Point position, CelSprite cel, int frame, bool skipColorIndexZero = true); } // namespace devilution diff --git a/Source/engine/render/cl2_render.cpp b/Source/engine/render/cl2_render.cpp index 351420213..e2d0b8715 100644 --- a/Source/engine/render/cl2_render.cpp +++ b/Source/engine/render/cl2_render.cpp @@ -69,7 +69,7 @@ BlitCommand Cl2GetBlitCommand(const uint8_t *src) * @param nDataSize Size of CL2 in bytes * @param nWidth Width of sprite */ -void Cl2BlitSafe(const Surface &out, Point position, const byte *pRLEBytes, int nDataSize, int nWidth) +void Cl2Blit(const Surface &out, Point position, const byte *pRLEBytes, int nDataSize, int nWidth) { DoRenderBackwards( out, position, reinterpret_cast(pRLEBytes), nDataSize, nWidth, BlitDirect {}); @@ -85,13 +85,19 @@ void Cl2BlitSafe(const Surface &out, Point position, const byte *pRLEBytes, int * @param nWidth With of CL2 sprite * @param pTable Light color table */ -void Cl2BlitLightSafe(const Surface &out, Point position, const byte *pRLEBytes, int nDataSize, int nWidth, uint8_t *pTable) +void Cl2BlitTRN(const Surface &out, Point position, const byte *pRLEBytes, int nDataSize, int nWidth, uint8_t *pTable) { DoRenderBackwards( out, position, reinterpret_cast(pRLEBytes), nDataSize, nWidth, BlitWithMap { pTable }); } -template +void Cl2BlitBlendedTRN(const Surface &out, Point position, const byte *pRLEBytes, int nDataSize, int nWidth, uint8_t *pTable) +{ + DoRenderBackwards( + out, position, reinterpret_cast(pRLEBytes), nDataSize, nWidth, BlitBlendedWithMap { pTable }); +} + +template uint8_t *RenderCl2OutlinePixelsCheckFirstColumn( uint8_t *dst, int dstPitch, int dstX, const uint8_t *src, uint8_t width, uint8_t color) @@ -101,7 +107,7 @@ uint8_t *RenderCl2OutlinePixelsCheckFirstColumn( RenderOutlineForPixel( dst++, dstPitch, color); } else { - RenderOutlineForPixel( + RenderOutlineForPixel( dst++, dstPitch, *src++, color); } --width; @@ -110,7 +116,7 @@ uint8_t *RenderCl2OutlinePixelsCheckFirstColumn( if (Fill) { RenderOutlineForPixel(dst++, dstPitch, color); } else { - RenderOutlineForPixel(dst++, dstPitch, *src++, color); + RenderOutlineForPixel(dst++, dstPitch, *src++, color); } --width; } @@ -118,14 +124,14 @@ uint8_t *RenderCl2OutlinePixelsCheckFirstColumn( if (Fill) { RenderOutlineForPixels(dst, dstPitch, width, color); } else { - RenderOutlineForPixels(dst, dstPitch, width, src, color); + RenderOutlineForPixels(dst, dstPitch, width, src, color); } dst += width; } return dst; } -template +template uint8_t *RenderCl2OutlinePixelsCheckLastColumn( uint8_t *dst, int dstPitch, int dstX, int dstW, const uint8_t *src, uint8_t width, uint8_t color) @@ -138,7 +144,7 @@ uint8_t *RenderCl2OutlinePixelsCheckLastColumn( if (Fill) { RenderOutlineForPixels(dst, dstPitch, width, color); } else { - RenderOutlineForPixels(dst, dstPitch, width, src, color); + RenderOutlineForPixels(dst, dstPitch, width, src, color); src += width; } dst += width; @@ -147,44 +153,44 @@ uint8_t *RenderCl2OutlinePixelsCheckLastColumn( if (Fill) { RenderOutlineForPixel(dst++, dstPitch, color); } else { - RenderOutlineForPixel(dst++, dstPitch, *src++, color); + RenderOutlineForPixel(dst++, dstPitch, *src++, color); } } if (oobPixel) { if (Fill) { RenderOutlineForPixel(dst++, dstPitch, color); } else { - RenderOutlineForPixel(dst++, dstPitch, *src++, color); + RenderOutlineForPixel(dst++, dstPitch, *src++, color); } } return dst; } -template +template uint8_t *RenderCl2OutlinePixels( uint8_t *dst, int dstPitch, int dstX, int dstW, const uint8_t *src, uint8_t width, uint8_t color) { - if (Fill && *src == 0) + if (SkipColorIndexZero && Fill && *src == 0) return dst + width; if (CheckFirstColumn && dstX <= 0) { - return RenderCl2OutlinePixelsCheckFirstColumn( + return RenderCl2OutlinePixelsCheckFirstColumn( dst, dstPitch, dstX, src, width, color); } if (CheckLastColumn && dstX + width >= dstW) { - return RenderCl2OutlinePixelsCheckLastColumn( + return RenderCl2OutlinePixelsCheckLastColumn( dst, dstPitch, dstX, dstW, src, width, color); } if (Fill) { RenderOutlineForPixels(dst, dstPitch, width, color); } else { - RenderOutlineForPixels(dst, dstPitch, width, src, color); + RenderOutlineForPixels(dst, dstPitch, width, src, color); } return dst + width; } -template const uint8_t *RenderCl2OutlineRowClipped( // NOLINT(readability-function-cognitive-complexity) const Surface &out, Point position, const uint8_t *src, std::size_t srcWidth, @@ -198,11 +204,11 @@ const uint8_t *RenderCl2OutlineRowClipped( // NOLINT(readability-function-cognit const auto renderPixels = [&](bool fill, uint8_t w) { if (fill) { - dst = RenderCl2OutlinePixels( + dst = RenderCl2OutlinePixels( dst, dstPitch, position.x, out.w(), src, w, color); ++src; } else { - dst = RenderCl2OutlinePixels( + dst = RenderCl2OutlinePixels( dst, dstPitch, position.x, out.w(), src, w, color); src += v; } @@ -276,6 +282,7 @@ const uint8_t *RenderCl2OutlineRowClipped( // NOLINT(readability-function-cognit return src; } +template void RenderCl2OutlineClippedY(const Surface &out, Point position, RenderSrcBackwards src, // NOLINT(readability-function-cognitive-complexity) uint8_t color) { @@ -289,7 +296,7 @@ void RenderCl2OutlineClippedY(const Surface &out, Point position, RenderSrcBackw if (position.y == dstHeight) { // After-bottom line - can only draw north. - src.begin = RenderCl2OutlineRowClipped( + src.begin = RenderCl2OutlineRowClipped( out, position, src.begin, src.width, clipX, color, skipSize); position.y -= static_cast(skipSize.wholeLines); } @@ -298,13 +305,13 @@ void RenderCl2OutlineClippedY(const Surface &out, Point position, RenderSrcBackw if (position.y + 1 == dstHeight) { // Bottom line - cannot draw south. - src.begin = RenderCl2OutlineRowClipped( + src.begin = RenderCl2OutlineRowClipped( out, position, src.begin, src.width, clipX, color, skipSize); position.y -= static_cast(skipSize.wholeLines); } while (position.y > 0 && src.begin != src.end) { - src.begin = RenderCl2OutlineRowClipped( + src.begin = RenderCl2OutlineRowClipped( out, position, src.begin, src.width, clipX, color, skipSize); position.y -= static_cast(skipSize.wholeLines); } @@ -312,7 +319,7 @@ void RenderCl2OutlineClippedY(const Surface &out, Point position, RenderSrcBackw return; if (position.y == 0) { - src.begin = RenderCl2OutlineRowClipped( + src.begin = RenderCl2OutlineRowClipped( out, position, src.begin, src.width, clipX, color, skipSize); position.y -= static_cast(skipSize.wholeLines); } @@ -321,11 +328,12 @@ void RenderCl2OutlineClippedY(const Surface &out, Point position, RenderSrcBackw if (position.y == -1) { // Special case: the top of the sprite is 1px below the last line, render just the outline above. - RenderCl2OutlineRowClipped( + RenderCl2OutlineRowClipped( out, position, src.begin, src.width, clipX, color, skipSize); } } +template void RenderCl2OutlineClippedXY(const Surface &out, Point position, RenderSrcBackwards src, // NOLINT(readability-function-cognitive-complexity) uint8_t color) { @@ -348,13 +356,13 @@ void RenderCl2OutlineClippedXY(const Surface &out, Point position, RenderSrcBack if (position.y == dstHeight) { // After-bottom line - can only draw north. if (position.x <= 0) { - src.begin = RenderCl2OutlineRowClipped(out, position, src.begin, src.width, clipX, color, skipSize); } else if (position.x + clipX.width >= out.w()) { - src.begin = RenderCl2OutlineRowClipped(out, position, src.begin, src.width, clipX, color, skipSize); } else { - src.begin = RenderCl2OutlineRowClipped(out, position, src.begin, src.width, clipX, color, skipSize); } position.y -= static_cast(skipSize.wholeLines); @@ -365,15 +373,15 @@ void RenderCl2OutlineClippedXY(const Surface &out, Point position, RenderSrcBack if (position.y + 1 == dstHeight) { // Bottom line - cannot draw south. if (position.x <= 0) { - src.begin = RenderCl2OutlineRowClipped( out, position, src.begin, src.width, clipX, color, skipSize); } else if (position.x + clipX.width >= out.w()) { - src.begin = RenderCl2OutlineRowClipped( out, position, src.begin, src.width, clipX, color, skipSize); } else { - src.begin = RenderCl2OutlineRowClipped( out, position, src.begin, src.width, clipX, color, skipSize); } @@ -382,21 +390,21 @@ void RenderCl2OutlineClippedXY(const Surface &out, Point position, RenderSrcBack if (position.x <= 0) { while (position.y > 0 && src.begin != src.end) { - src.begin = RenderCl2OutlineRowClipped( out, position, src.begin, src.width, clipX, color, skipSize); position.y -= static_cast(skipSize.wholeLines); } } else if (position.x + clipX.width >= out.w()) { while (position.y > 0 && src.begin != src.end) { - src.begin = RenderCl2OutlineRowClipped( out, position, src.begin, src.width, clipX, color, skipSize); position.y -= static_cast(skipSize.wholeLines); } } else { while (position.y > 0 && src.begin != src.end) { - src.begin = RenderCl2OutlineRowClipped( out, position, src.begin, src.width, clipX, color, skipSize); position.y -= static_cast(skipSize.wholeLines); @@ -407,15 +415,15 @@ void RenderCl2OutlineClippedXY(const Surface &out, Point position, RenderSrcBack if (position.y == 0) { if (position.x <= 0) { - src.begin = RenderCl2OutlineRowClipped( out, position, src.begin, src.width, clipX, color, skipSize); } else if (position.x + clipX.width >= out.w()) { - src.begin = RenderCl2OutlineRowClipped( out, position, src.begin, src.width, clipX, color, skipSize); } else { - src.begin = RenderCl2OutlineRowClipped( out, position, src.begin, src.width, clipX, color, skipSize); } @@ -427,26 +435,27 @@ void RenderCl2OutlineClippedXY(const Surface &out, Point position, RenderSrcBack if (position.y == -1) { // Before-top line - can only draw south. if (position.x <= 0) { - src.begin = RenderCl2OutlineRowClipped(out, position, src.begin, src.width, clipX, color, skipSize); } else if (position.x + clipX.width >= out.w()) { - src.begin = RenderCl2OutlineRowClipped(out, position, src.begin, src.width, clipX, color, skipSize); } else { - src.begin = RenderCl2OutlineRowClipped(out, position, src.begin, src.width, clipX, color, skipSize); } } } +template void RenderCl2Outline(const Surface &out, Point position, const uint8_t *src, std::size_t srcSize, std::size_t srcWidth, uint8_t color) { RenderSrcBackwards srcForBackwards { src, src + srcSize, static_cast(srcWidth) }; if (position.x > 0 && position.x + static_cast(srcWidth) < static_cast(out.w())) { - RenderCl2OutlineClippedY(out, position, srcForBackwards, color); + RenderCl2OutlineClippedY(out, position, srcForBackwards, color); } else { - RenderCl2OutlineClippedXY(out, position, srcForBackwards, color); + RenderCl2OutlineClippedXY(out, position, srcForBackwards, color); } } @@ -485,6 +494,42 @@ void Cl2ApplyTrans(byte *p, const std::array &ttbl, int numFrames) } } +std::pair Cl2MeasureSolidHorizontalBounds(CelSprite cel, int frame) +{ + int nDataSize; + const byte *src = CelGetFrameClipped(cel.Data(), frame, &nDataSize); + const auto *end = &src[nDataSize]; + const int celWidth = cel.Width(frame); + + int xBegin = celWidth; + int xEnd = 0; + int xCur = 0; + while (src < end) { + while (xCur < celWidth) { + auto val = static_cast(*src++); + if (!IsCl2Opaque(val)) { + xCur += val; + continue; + } + if (IsCl2OpaqueFill(val)) { + val = GetCl2OpaqueFillWidth(val); + ++src; + } else { + val = GetCl2OpaquePixelsWidth(val); + src += val; + } + xBegin = std::min(xBegin, xCur); + xCur += val; + xEnd = std::max(xEnd, xCur); + } + while (xCur >= celWidth) + xCur -= celWidth; + if (xBegin == 0 && xEnd == celWidth) + break; + } + return { xBegin, xEnd }; +} + void Cl2Draw(const Surface &out, Point position, CelSprite cel, int frame) { assert(frame >= 0); @@ -492,7 +537,7 @@ void Cl2Draw(const Surface &out, Point position, CelSprite cel, int frame) int nDataSize; const byte *pRLEBytes = CelGetFrameClipped(cel.Data(), frame, &nDataSize); - Cl2BlitSafe(out, position, pRLEBytes, nDataSize, cel.Width(frame)); + Cl2Blit(out, position, pRLEBytes, nDataSize, cel.Width(frame)); } void Cl2DrawOutline(const Surface &out, uint8_t col, Point position, CelSprite cel, int frame) @@ -502,7 +547,17 @@ void Cl2DrawOutline(const Surface &out, uint8_t col, Point position, CelSprite c int nDataSize; const byte *src = CelGetFrameClipped(cel.Data(), frame, &nDataSize); - RenderCl2Outline(out, position, reinterpret_cast(src), nDataSize, cel.Width(frame), col); + RenderCl2Outline(out, position, reinterpret_cast(src), nDataSize, cel.Width(frame), col); +} + +void Cl2DrawOutlineSkipColorZero(const Surface &out, uint8_t col, Point position, CelSprite cel, int frame) +{ + assert(frame >= 0); + + int nDataSize; + const byte *src = CelGetFrameClipped(cel.Data(), frame, &nDataSize); + + RenderCl2Outline(out, position, reinterpret_cast(src), nDataSize, cel.Width(frame), col); } void Cl2DrawTRN(const Surface &out, Point position, CelSprite cel, int frame, uint8_t *trn) @@ -511,20 +566,16 @@ void Cl2DrawTRN(const Surface &out, Point position, CelSprite cel, int frame, ui int nDataSize; const byte *pRLEBytes = CelGetFrameClipped(cel.Data(), frame, &nDataSize); - Cl2BlitLightSafe(out, position, pRLEBytes, nDataSize, cel.Width(frame), trn); + Cl2BlitTRN(out, position, pRLEBytes, nDataSize, cel.Width(frame), trn); } -void Cl2DrawLight(const Surface &out, Point position, CelSprite cel, int frame) +void Cl2DrawBlendedTRN(const Surface &out, Point position, CelSprite cel, int frame, uint8_t *trn) { assert(frame >= 0); int nDataSize; const byte *pRLEBytes = CelGetFrameClipped(cel.Data(), frame, &nDataSize); - - if (LightTableIndex != 0) - Cl2BlitLightSafe(out, position, pRLEBytes, nDataSize, cel.Width(frame), &LightTables[LightTableIndex * 256]); - else - Cl2BlitSafe(out, position, pRLEBytes, nDataSize, cel.Width(frame)); + Cl2BlitBlendedTRN(out, position, pRLEBytes, nDataSize, cel.Width(frame), trn); } } // namespace devilution diff --git a/Source/engine/render/cl2_render.hpp b/Source/engine/render/cl2_render.hpp index 2dadc0cc2..ffa473b50 100644 --- a/Source/engine/render/cl2_render.hpp +++ b/Source/engine/render/cl2_render.hpp @@ -5,12 +5,15 @@ */ #pragma once -#include #include +#include +#include + #include "engine.h" #include "engine/cel_sprite.hpp" #include "engine/point.hpp" +#include "lighting.h" namespace devilution { @@ -32,7 +35,7 @@ void Cl2ApplyTrans(byte *p, const std::array &ttbl, int numFrames) void Cl2Draw(const Surface &out, Point position, CelSprite cel, int frame); /** - * @brief Blit a solid colder shape one pixel larger than the given sprite shape, to the given buffer at the given coordianates + * @brief Blit a solid colder shape one pixel larger than the given sprite shape, to the given buffer at the given coordinates * @param col Color index from current palette * @param out Output buffer * @param position Target buffer coordinate @@ -41,6 +44,17 @@ void Cl2Draw(const Surface &out, Point position, CelSprite cel, int frame); */ void Cl2DrawOutline(const Surface &out, uint8_t col, Point position, CelSprite cel, int frame); +/** + * @brief Same as `Cl2DrawOutline` but considers colors with index 0 (usually shadows) to be transparent. + * + * @param col Color index from current palette + * @param out Output buffer + * @param position Target buffer coordinate + * @param cel CL2 buffer + * @param frame CL2 frame number + */ +void Cl2DrawOutlineSkipColorZero(const Surface &out, uint8_t col, Point position, CelSprite cel, int frame); + /** * @brief Blit CL2 sprite, and apply given TRN to the given buffer at the given coordinates * @param out Output buffer @@ -51,6 +65,11 @@ void Cl2DrawOutline(const Surface &out, uint8_t col, Point position, CelSprite c */ void Cl2DrawTRN(const Surface &out, Point position, CelSprite cel, int frame, uint8_t *trn); +void Cl2DrawBlendedTRN(const Surface &out, Point position, CelSprite cel, int frame, uint8_t *trn); + +// defined in scrollrt.cpp +extern int LightTableIndex; + /** * @brief Blit CL2 sprite, and apply lighting, to the given buffer at the given coordinates * @param out Output buffer @@ -58,6 +77,23 @@ void Cl2DrawTRN(const Surface &out, Point position, CelSprite cel, int frame, ui * @param cel CL2 buffer * @param frame CL2 frame number */ -void Cl2DrawLight(const Surface &out, Point position, CelSprite cel, int frame); +inline void Cl2DrawLight(const Surface &out, Point position, CelSprite cel, int frame) +{ + if (LightTableIndex != 0) + Cl2DrawTRN(out, position, cel, frame, &LightTables[LightTableIndex * 256]); + else + Cl2Draw(out, position, cel, frame); +} + +inline void Cl2DrawLightBlended(const Surface &out, Point position, CelSprite cel, int frame) +{ + Cl2DrawBlendedTRN(out, position, cel, frame, &LightTables[LightTableIndex * 256]); +} + +/** + * Returns a pair of X coordinates containing the start (inclusive) and end (exclusive) + * of fully transparent columns in the sprite. + */ +std::pair Cl2MeasureSolidHorizontalBounds(CelSprite cel, int frame = 0); } // namespace devilution diff --git a/Source/engine/render/scrollrt.cpp b/Source/engine/render/scrollrt.cpp index 5b8c0a74f..5e38859f5 100644 --- a/Source/engine/render/scrollrt.cpp +++ b/Source/engine/render/scrollrt.cpp @@ -13,7 +13,6 @@ #include "dead.h" #include "doom.h" #include "engine/dx.h" -#include "engine/render/cel_render.hpp" #include "engine/render/cl2_render.hpp" #include "engine/render/dun_render.hpp" #include "engine/render/text_render.hpp" @@ -306,7 +305,7 @@ void DrawCursor(const Surface &out) Clip(sgdwCursY, sgdwCursHgt, out.h()); BlitCursor(sgSaveBack, sgdwCursWdt, out.at(sgdwCursX, sgdwCursY), out.pitch()); - CelDrawCursor(out, MousePosition + Displacement { 0, cursSize.height - 1 }, pcurs); + DrawSoftwareCursor(out, MousePosition + Displacement { 0, cursSize.height - 1 }, pcurs); } /** @@ -546,7 +545,7 @@ void DrawPlayer(const Surface &out, const Player &player, Point tilePosition, Po } if (pcursplr >= 0 && pcursplr < MAX_PLRS && &player == &Players[pcursplr]) - Cl2DrawOutline(out, 165, spriteBufferPosition, *sprite, nCel); + Cl2DrawOutlineSkipColorZero(out, 165, spriteBufferPosition, *sprite, nCel); if (&player == MyPlayer) { Cl2Draw(out, spriteBufferPosition, *sprite, nCel); @@ -636,12 +635,12 @@ void DrawObject(const Surface &out, Point tilePosition, Point targetBufferPositi CelSprite cel { objectToDraw._oAnimData, objectToDraw._oAnimWidth }; if (pcursobj != -1 && &objectToDraw == &Objects[pcursobj]) { - CelBlitOutlineTo(out, 194, screenPosition, cel, nCel); + Cl2DrawOutlineSkipColorZero(out, 194, screenPosition, cel, nCel); } if (objectToDraw._oLight) { - CelClippedDrawLightTo(out, screenPosition, cel, nCel); + Cl2DrawLight(out, screenPosition, cel, nCel); } else { - CelClippedDrawTo(out, screenPosition, cel, nCel); + Cl2Draw(out, screenPosition, cel, nCel); } } @@ -733,9 +732,9 @@ void DrawItem(const Surface &out, Point tilePosition, Point targetBufferPosition int px = targetBufferPosition.x - CalculateWidth2(cel->Width()); const Point position { px, targetBufferPosition.y }; if (bItem - 1 == pcursitem || AutoMapShowItems) { - CelBlitOutlineTo(out, GetOutlineColor(item, false), position, *cel, nCel); + Cl2DrawOutlineSkipColorZero(out, GetOutlineColor(item, false), position, *cel, nCel); } - CelClippedDrawLightTo(out, position, *cel, nCel); + Cl2DrawLight(out, position, *cel, nCel); if (item.AnimInfo.currentFrame == item.AnimInfo.numberOfFrames - 1 || item._iCurs == ICURS_MAGIC_ROCK) AddItemToLabelQueue(bItem - 1, px, targetBufferPosition.y); } @@ -756,10 +755,10 @@ void DrawMonsterHelper(const Surface &out, Point tilePosition, Point targetBuffe const Point position { px, targetBufferPosition.y }; const CelSprite sprite = towner.Sprite(); if (mi == pcursmonst) { - CelBlitOutlineTo(out, 166, position, sprite, towner._tAnimFrame); + Cl2DrawOutlineSkipColorZero(out, 166, position, sprite, towner._tAnimFrame); } assert(towner._tAnimData); - CelClippedDrawTo(out, position, sprite, towner._tAnimFrame); + Cl2Draw(out, position, sprite, towner._tAnimFrame); return; } @@ -785,7 +784,7 @@ void DrawMonsterHelper(const Surface &out, Point tilePosition, Point targetBuffe const Point monsterRenderPosition { targetBufferPosition + offset - Displacement { CalculateWidth2(cel.Width()), 0 } }; if (mi == pcursmonst) { - Cl2DrawOutline(out, 233, monsterRenderPosition, cel, monster.animInfo.getFrameToUseForRendering()); + Cl2DrawOutlineSkipColorZero(out, 233, monsterRenderPosition, cel, monster.animInfo.getFrameToUseForRendering()); } DrawMonster(out, tilePosition, monsterRenderPosition, monster); } @@ -832,7 +831,7 @@ void DrawDungeon(const Surface &out, Point tilePosition, Point targetBufferPosit #ifdef _DEBUG if (DebugVision && IsTileLit(tilePosition)) { - CelClippedDrawTo(out, targetBufferPosition, CelSprite { *pSquareCel }, 1); + Cl2Draw(out, targetBufferPosition, CelSprite { *pSquareCel }, 1); } #endif @@ -886,7 +885,11 @@ void DrawDungeon(const Surface &out, Point tilePosition, Point targetBufferPosit cel_transparency_active = false; // Turn transparency off here for debugging } #endif - CelClippedBlitLightTransTo(out, targetBufferPosition, CelSprite { *pSpecialCels }, bArch - 1); + if (cel_transparency_active) { + Cl2DrawLightBlended(out, targetBufferPosition, CelSprite { *pSpecialCels }, bArch - 1); + } else { + Cl2DrawLight(out, targetBufferPosition, CelSprite { *pSpecialCels }, bArch - 1); + } #ifdef _DEBUG if ((SDL_GetModState() & KMOD_ALT) != 0) { cel_transparency_active = TransList[bMap]; // Turn transparency back to its normal state @@ -900,7 +903,7 @@ void DrawDungeon(const Surface &out, Point tilePosition, Point targetBufferPosit if (tilePosition.x > 0 && tilePosition.y > 0 && targetBufferPosition.y > TILE_HEIGHT) { char bArch = dSpecial[tilePosition.x - 1][tilePosition.y - 1]; if (bArch != 0) { - CelDrawTo(out, targetBufferPosition + Displacement { 0, -TILE_HEIGHT }, CelSprite { *pSpecialCels }, bArch - 1); + Cl2Draw(out, targetBufferPosition + Displacement { 0, -TILE_HEIGHT }, CelSprite { *pSpecialCels }, bArch - 1); } } } diff --git a/Source/engine/render/text_render.cpp b/Source/engine/render/text_render.cpp index 129b4ba87..8ddc48445 100644 --- a/Source/engine/render/text_render.cpp +++ b/Source/engine/render/text_render.cpp @@ -15,13 +15,13 @@ #include "DiabloUI/art_draw.h" #include "DiabloUI/diabloui.h" #include "DiabloUI/ui_item.h" -#include "cel_render.hpp" #include "engine.h" #include "engine/load_cel.hpp" #include "engine/load_file.hpp" #include "engine/load_pcx.hpp" #include "engine/palette.h" #include "engine/point.hpp" +#include "engine/render/cl2_render.hpp" #include "pcx_render.hpp" #include "utils/display.h" #include "utils/language.h" @@ -405,7 +405,7 @@ int DoDrawString(const Surface &out, string_view text, Rectangle rect, Point &ch void LoadSmallSelectionSpinner() { - pSPentSpn2Cels = LoadCel("Data\\PentSpn2.CEL", 12); + pSPentSpn2Cels = LoadCelAsCl2("Data\\PentSpn2.CEL", 12); } void UnloadFonts(GameFontTables size, text_color color) @@ -652,7 +652,7 @@ uint32_t DrawString(const Surface &out, string_view text, const Rectangle &rect, const int bytesDrawn = DoDrawString(out, text, rect, characterPosition, spacing, lineHeight, lineWidth, rightMargin, bottomMargin, flags, size, color); if (HasAnyOf(flags, UiFlags::PentaCursor)) { - CelDrawTo(out, characterPosition + Displacement { 0, lineHeight - BaseLineOffset[size] }, CelSprite { *pSPentSpn2Cels }, PentSpn2Spin()); + Cl2Draw(out, characterPosition + Displacement { 0, lineHeight - BaseLineOffset[size] }, CelSprite { *pSPentSpn2Cels }, PentSpn2Spin()); } else if (HasAnyOf(flags, UiFlags::TextCursor) && GetAnimationFrame(2, 500) != 0) { DrawFont(out, characterPosition, LoadFont(size, color, 0), color, '|'); } @@ -760,7 +760,7 @@ void DrawStringWithColors(const Surface &out, string_view fmt, DrawStringFormatA } if (HasAnyOf(flags, UiFlags::PentaCursor)) { - CelDrawTo(out, characterPosition + Displacement { 0, lineHeight - BaseLineOffset[size] }, CelSprite { *pSPentSpn2Cels }, PentSpn2Spin()); + Cl2Draw(out, characterPosition + Displacement { 0, lineHeight - BaseLineOffset[size] }, CelSprite { *pSPentSpn2Cels }, PentSpn2Spin()); } else if (HasAnyOf(flags, UiFlags::TextCursor) && GetAnimationFrame(2, 500) != 0) { DrawFont(out, characterPosition, LoadFont(size, color, 0), color, '|'); } diff --git a/Source/error.cpp b/Source/error.cpp index d88e24ae3..b0e6d7b19 100644 --- a/Source/error.cpp +++ b/Source/error.cpp @@ -9,7 +9,7 @@ #include "error.h" #include "DiabloUI/ui_flags.hpp" -#include "engine/render/cel_render.hpp" +#include "engine/render/cl2_render.hpp" #include "engine/render/text_render.hpp" #include "panels/info_box.hpp" #include "stores.h" @@ -145,21 +145,21 @@ void DrawDiabloMsg(const Surface &out) int dialogStartY = ((gnScreenHeight - GetMainPanel().size.height) / 2) - (ErrorWindowHeight / 2) + 9; CelSprite sprite { *pSTextSlidCels }; - CelDrawTo(out, { uiRectanglePosition.x + 101, dialogStartY }, sprite, 0); - CelDrawTo(out, { uiRectanglePosition.x + 101, dialogStartY + ErrorWindowHeight - 6 }, sprite, 1); - CelDrawTo(out, { uiRectanglePosition.x + 527, dialogStartY + ErrorWindowHeight - 6 }, sprite, 2); - CelDrawTo(out, { uiRectanglePosition.x + 527, dialogStartY }, sprite, 3); + Cl2Draw(out, { uiRectanglePosition.x + 101, dialogStartY }, sprite, 0); + Cl2Draw(out, { uiRectanglePosition.x + 101, dialogStartY + ErrorWindowHeight - 6 }, sprite, 1); + Cl2Draw(out, { uiRectanglePosition.x + 527, dialogStartY + ErrorWindowHeight - 6 }, sprite, 2); + Cl2Draw(out, { uiRectanglePosition.x + 527, dialogStartY }, sprite, 3); int sx = uiRectanglePosition.x + 109; for (int i = 0; i < 35; i++) { - CelDrawTo(out, { sx, dialogStartY }, sprite, 4); - CelDrawTo(out, { sx, dialogStartY + ErrorWindowHeight - 6 }, sprite, 6); + Cl2Draw(out, { sx, dialogStartY }, sprite, 4); + Cl2Draw(out, { sx, dialogStartY + ErrorWindowHeight - 6 }, sprite, 6); sx += 12; } int drawnYborder = 12; while ((drawnYborder + 12) < ErrorWindowHeight) { - CelDrawTo(out, { uiRectanglePosition.x + 101, dialogStartY + drawnYborder }, sprite, 5); - CelDrawTo(out, { uiRectanglePosition.x + 527, dialogStartY + drawnYborder }, sprite, 7); + Cl2Draw(out, { uiRectanglePosition.x + 101, dialogStartY + drawnYborder }, sprite, 5); + Cl2Draw(out, { uiRectanglePosition.x + 527, dialogStartY + drawnYborder }, sprite, 7); drawnYborder += 12; } diff --git a/Source/gmenu.cpp b/Source/gmenu.cpp index 3ed34a59f..40be49c77 100644 --- a/Source/gmenu.cpp +++ b/Source/gmenu.cpp @@ -12,7 +12,7 @@ #include "engine.h" #include "engine/cel_sprite.hpp" #include "engine/load_cel.hpp" -#include "engine/render/cel_render.hpp" +#include "engine/render/cl2_render.hpp" #include "engine/render/text_render.hpp" #include "miniwin/misc_msg.h" #include "options.h" @@ -108,12 +108,12 @@ void GmenuDrawMenuItem(const Surface &out, TMenuItem *pItem, int y) if ((pItem->dwFlags & GMENU_SLIDER) != 0) { int uiPositionX = GetUIRectangle().position.x; int x = 16 + w / 2; - CelDrawTo(out, { x + uiPositionX, y + 40 }, CelSprite { *optbar_cel }, 0); + Cl2Draw(out, { x + uiPositionX, y + 40 }, CelSprite { *optbar_cel }, 0); uint16_t step = pItem->dwFlags & 0xFFF; uint16_t steps = std::max((pItem->dwFlags & 0xFFF000) >> 12, 2); uint16_t pos = step * 256 / steps; GmenuClearBuffer(out, x + 2 + uiPositionX, y + 38, pos + 13, 28); - CelDrawTo(out, { x + 2 + pos + uiPositionX, y + 38 }, CelSprite { *option_cel }, 0); + Cl2Draw(out, { x + 2 + pos + uiPositionX, y + 38 }, CelSprite { *option_cel }, 0); } int x = (gnScreenWidth - w) / 2; @@ -121,8 +121,8 @@ void GmenuDrawMenuItem(const Surface &out, TMenuItem *pItem, int y) DrawString(out, _(pItem->pszStr), Point { x, y }, style | UiFlags::FontSize46, 2); if (pItem == sgpCurrItem) { CelSprite sprite { *PentSpin_cel }; - CelDrawTo(out, { x - 54, y + 51 }, sprite, PentSpn2Spin()); - CelDrawTo(out, { x + 4 + w, y + 51 }, sprite, PentSpn2Spin()); + Cl2Draw(out, { x - 54, y + 51 }, sprite, PentSpn2Spin()); + Cl2Draw(out, { x + 4 + w, y + 51 }, sprite, PentSpn2Spin()); } } @@ -195,12 +195,12 @@ void gmenu_init_menu() return; if (gbIsHellfire) - sgpLogo = LoadCel("Data\\hf_logo3.CEL", 430); + sgpLogo = LoadCelAsCl2("Data\\hf_logo3.CEL", 430); else - sgpLogo = LoadCel("Data\\Diabsmal.CEL", 296); - PentSpin_cel = LoadCel("Data\\PentSpin.CEL", 48); - option_cel = LoadCel("Data\\option.CEL", 27); - optbar_cel = LoadCel("Data\\optbar.CEL", 287); + sgpLogo = LoadCelAsCl2("Data\\Diabsmal.CEL", 296); + PentSpin_cel = LoadCelAsCl2("Data\\PentSpin.CEL", 48); + option_cel = LoadCelAsCl2("Data\\option.CEL", 27); + optbar_cel = LoadCelAsCl2("Data\\optbar.CEL", 287); } bool gmenu_is_active() @@ -247,7 +247,7 @@ void gmenu_draw(const Surface &out) } int uiPositionY = GetUIRectangle().position.y; CelSprite sprite { *sgpLogo }; - CelDrawTo(out, { (gnScreenWidth - sprite.Width()) / 2, 102 + uiPositionY }, sprite, LogoAnim_frame); + Cl2Draw(out, { (gnScreenWidth - sprite.Width()) / 2, 102 + uiPositionY }, sprite, LogoAnim_frame); int y = 110 + uiPositionY; TMenuItem *i = sgpCurrentMenu; if (sgpCurrentMenu->fnMenu != nullptr) { diff --git a/Source/hwcursor.cpp b/Source/hwcursor.cpp index 2b4883876..e7de6a19f 100644 --- a/Source/hwcursor.cpp +++ b/Source/hwcursor.cpp @@ -119,7 +119,7 @@ bool SetHardwareCursorFromSprite(int pcurs) constexpr std::uint8_t TransparentColor = 1; SDL_FillRect(out.surface, nullptr, TransparentColor); SDL_SetColorKey(out.surface, 1, TransparentColor); - CelDrawCursor(out, { outlineWidth, size.height - outlineWidth }, pcurs); + DrawSoftwareCursor(out, { outlineWidth, size.height - outlineWidth }, pcurs); const bool result = SetHardwareCursor(out.surface, isItem ? HotpointPosition::Center : HotpointPosition::TopLeft); return result; diff --git a/Source/interfac.cpp b/Source/interfac.cpp index 40cbb22f9..5254fe92b 100644 --- a/Source/interfac.cpp +++ b/Source/interfac.cpp @@ -16,7 +16,7 @@ #include "engine/load_pcx.hpp" #include "engine/palette.h" #include "engine/pcx_sprite.hpp" -#include "engine/render/cel_render.hpp" +#include "engine/render/cl2_render.hpp" #include "engine/render/pcx_render.hpp" #include "hwcursor.hpp" #include "init.h" @@ -164,7 +164,7 @@ void LoadCutsceneBackground(interface_mode uMsg) } assert(!sgpBackCel); - sgpBackCel = LoadCel(celPath, 640); + sgpBackCel = LoadCelAsCl2(celPath, 640); LoadPalette(palPath); sgdwProgress = 0; @@ -184,7 +184,7 @@ void DrawCutsceneBackground() const PcxSprite sprite { *ArtCutsceneWidescreen }; RenderPcxSprite(out, sprite, { uiRectangle.position.x - (sprite.width() - uiRectangle.size.width) / 2, uiRectangle.position.y }); } - CelDrawTo(out, { uiRectangle.position.x, 480 - 1 + uiRectangle.position.y }, CelSprite { *sgpBackCel }, 0); + Cl2Draw(out, { uiRectangle.position.x, 480 - 1 + uiRectangle.position.y }, CelSprite { *sgpBackCel }, 0); } void DrawCutsceneForeground() diff --git a/Source/inv.cpp b/Source/inv.cpp index c5edb58eb..e2cac611e 100644 --- a/Source/inv.cpp +++ b/Source/inv.cpp @@ -13,7 +13,7 @@ #include "cursor.h" #include "engine/cel_sprite.hpp" #include "engine/load_cel.hpp" -#include "engine/render/cel_render.hpp" +#include "engine/render/cl2_render.hpp" #include "engine/render/text_render.hpp" #include "engine/size.hpp" #include "hwcursor.hpp" @@ -1064,24 +1064,24 @@ void InitInv() switch (MyPlayer->_pClass) { case HeroClass::Warrior: case HeroClass::Barbarian: - pInvCels = LoadCel("Data\\Inv\\Inv.CEL", static_cast(SidePanelSize.width)); + pInvCels = LoadCelAsCl2("Data\\Inv\\Inv.CEL", static_cast(SidePanelSize.width)); break; case HeroClass::Rogue: case HeroClass::Bard: - pInvCels = LoadCel("Data\\Inv\\Inv_rog.CEL", static_cast(SidePanelSize.width)); + pInvCels = LoadCelAsCl2("Data\\Inv\\Inv_rog.CEL", static_cast(SidePanelSize.width)); break; case HeroClass::Sorcerer: - pInvCels = LoadCel("Data\\Inv\\Inv_Sor.CEL", static_cast(SidePanelSize.width)); + pInvCels = LoadCelAsCl2("Data\\Inv\\Inv_Sor.CEL", static_cast(SidePanelSize.width)); break; case HeroClass::Monk: - pInvCels = LoadCel(!gbIsSpawn ? "Data\\Inv\\Inv_Sor.CEL" : "Data\\Inv\\Inv.CEL", static_cast(SidePanelSize.width)); + pInvCels = LoadCelAsCl2(!gbIsSpawn ? "Data\\Inv\\Inv_Sor.CEL" : "Data\\Inv\\Inv.CEL", static_cast(SidePanelSize.width)); break; } } void DrawInv(const Surface &out) { - CelDrawTo(out, GetPanelPosition(UiPanels::Inventory, { 0, 351 }), CelSprite { *pInvCels }, 0); + Cl2Draw(out, GetPanelPosition(UiPanels::Inventory, { 0, 351 }), CelSprite { *pInvCels }, 0); Size slotSize[] = { { 2, 2 }, // head @@ -1129,10 +1129,10 @@ void DrawInv(const Surface &out) const Point position = GetPanelPosition(UiPanels::Inventory, { screenX, screenY }); if (pcursinvitem == slot) { - CelBlitOutlineTo(out, GetOutlineColor(myPlayer.InvBody[slot], true), position, cel, celFrame, false); + Cl2DrawOutline(out, GetOutlineColor(myPlayer.InvBody[slot], true), position, cel, celFrame); } - CelDrawItem(myPlayer.InvBody[slot], out, position, cel, celFrame); + DrawItem(myPlayer.InvBody[slot], out, position, cel, celFrame); if (slot == INVLOC_HAND_LEFT) { if (myPlayer.GetItemLocation(myPlayer.InvBody[slot]) == ILOC_TWOHAND) { @@ -1142,7 +1142,7 @@ void DrawInv(const Surface &out) const int dstX = GetRightPanel().position.x + slotPos[INVLOC_HAND_RIGHT].x + (frameSize.width == InventorySlotSizeInPixels.width ? INV_SLOT_HALF_SIZE_PX : 0) - 1; const int dstY = GetRightPanel().position.y + slotPos[INVLOC_HAND_RIGHT].y; - CelClippedBlitLightTransTo(out, { dstX, dstY }, cel, celFrame); + Cl2DrawLightBlended(out, { dstX, dstY }, cel, celFrame); cel_transparency_active = false; } @@ -1168,14 +1168,10 @@ void DrawInv(const Surface &out) const int celFrame = GetInvItemFrame(cursId); const Point position = GetPanelPosition(UiPanels::Inventory, InvRect[j + SLOTXY_INV_FIRST]) + Displacement { 0, -1 }; if (pcursinvitem == ii + INVITEM_INV_FIRST) { - CelBlitOutlineTo( - out, - GetOutlineColor(myPlayer.InvList[ii], true), - position, - cel, celFrame, false); + Cl2DrawOutline(out, GetOutlineColor(myPlayer.InvList[ii], true), position, cel, celFrame); } - CelDrawItem( + DrawItem( myPlayer.InvList[ii], out, position, @@ -1210,11 +1206,11 @@ void DrawInvBelt(const Surface &out) if (pcursinvitem == i + INVITEM_BELT_FIRST) { if (ControlMode == ControlTypes::KeyboardAndMouse || invflag) { - CelBlitOutlineTo(out, GetOutlineColor(myPlayer.SpdList[i], true), position, cel, celFrame, false); + Cl2DrawOutline(out, GetOutlineColor(myPlayer.SpdList[i], true), position, cel, celFrame); } } - CelDrawItem(myPlayer.SpdList[i], out, position, cel, celFrame); + DrawItem(myPlayer.SpdList[i], out, position, cel, celFrame); if (AllItemsList[myPlayer.SpdList[i].IDidx].iUsable && myPlayer.SpdList[i]._itype != ItemType::Gold) { diff --git a/Source/items.cpp b/Source/items.cpp index 49545adb5..4ca165df6 100644 --- a/Source/items.cpp +++ b/Source/items.cpp @@ -24,7 +24,7 @@ #include "engine/dx.h" #include "engine/load_cel.hpp" #include "engine/random.hpp" -#include "engine/render/cel_render.hpp" +#include "engine/render/cl2_render.hpp" #include "engine/render/text_render.hpp" #include "init.h" #include "inv_iterators.hpp" @@ -1757,7 +1757,7 @@ void PrintItemOil(char iDidx) void DrawUniqueInfoWindow(const Surface &out) { - CelDrawTo(out, GetPanelPosition(UiPanels::Inventory, { 24 - SidePanelSize.width, 327 }), CelSprite { *pSTextBoxCels }, 0); + Cl2Draw(out, GetPanelPosition(UiPanels::Inventory, { 24 - SidePanelSize.width, 327 }), CelSprite { *pSTextBoxCels }, 0); DrawHalfTransparentRectTo(out, GetRightPanel().position.x - SidePanelSize.width + 27, GetRightPanel().position.y + 28, 265, 297); } @@ -2282,7 +2282,7 @@ void InitItemGFX() int itemTypes = gbIsHellfire ? ITEMTYPES : 35; for (int i = 0; i < itemTypes; i++) { *BufCopy(arglist, "Items\\", ItemDropNames[i], ".CEL") = '\0'; - itemanims[i] = LoadCel(arglist, ItemAnimWidth); + itemanims[i] = LoadCelAsCl2(arglist, ItemAnimWidth); } memset(UniqueItemFlags, 0, sizeof(UniqueItemFlags)); } diff --git a/Source/minitext.cpp b/Source/minitext.cpp index 04e32943c..005123f5a 100644 --- a/Source/minitext.cpp +++ b/Source/minitext.cpp @@ -12,7 +12,7 @@ #include "engine/cel_sprite.hpp" #include "engine/dx.h" #include "engine/load_cel.hpp" -#include "engine/render/cel_render.hpp" +#include "engine/render/cl2_render.hpp" #include "engine/render/text_render.hpp" #include "textdat.h" #include "utils/language.h" @@ -126,7 +126,7 @@ void FreeQuestText() void InitQuestText() { - pTextBoxCels = LoadCel("Data\\TextBox.CEL", 591); + pTextBoxCels = LoadCelAsCl2("Data\\TextBox.CEL", 591); } void InitQTextMsg(_speech_id m) @@ -144,7 +144,7 @@ void InitQTextMsg(_speech_id m) void DrawQTextBack(const Surface &out) { const Point uiPosition = GetUIRectangle().position; - CelDrawTo(out, uiPosition + Displacement { 24, 327 }, CelSprite { *pTextBoxCels }, 0); + Cl2Draw(out, uiPosition + Displacement { 24, 327 }, CelSprite { *pTextBoxCels }, 0); DrawHalfTransparentRectTo(out, uiPosition.x + 27, uiPosition.y + 28, 585, 297); } diff --git a/Source/objects.cpp b/Source/objects.cpp index cde27e3df..cc48424ea 100644 --- a/Source/objects.cpp +++ b/Source/objects.cpp @@ -17,6 +17,7 @@ #ifdef _DEBUG #include "debug.h" #endif +#include "engine/load_cel.hpp" #include "engine/load_file.hpp" #include "engine/points_in_rectangle_range.hpp" #include "engine/random.hpp" @@ -4062,37 +4063,37 @@ bool IsItemBlockingObjectAtPosition(Point position) return false; } -void LoadLevelObjects(bool filesLoaded[65]) +void LoadLevelObjects(uint16_t filesWidths[65]) { if (HeadlessMode) return; for (const ObjectData objectData : AllObjects) { if (leveltype == objectData.olvltype) { - filesLoaded[objectData.ofindex] = true; + filesWidths[objectData.ofindex] = objectData.oAnimWidth; } } for (int i = OFILE_L1BRAZ; i <= OFILE_L5BOOKS; i++) { - if (!filesLoaded[i]) { + if (filesWidths[i] == 0) { continue; } ObjFileList[numobjfiles] = static_cast(i); char filestr[32]; *BufCopy(filestr, "Objects\\", ObjMasterLoadList[i], ".CEL") = '\0'; - pObjCels[numobjfiles] = LoadFileInMem(filestr); + pObjCels[numobjfiles] = LoadCelAsCl2(filestr, filesWidths[i]).data(); numobjfiles++; } } void InitObjectGFX() { - bool filesLoaded[65] = {}; + uint16_t filesWidths[65] = {}; if (IsAnyOf(currlevel, 4, 8, 12)) { - filesLoaded[OFILE_BKSLBRNT] = true; - filesLoaded[OFILE_CANDLE2] = true; + filesWidths[OFILE_BKSLBRNT] = AllObjects[OBJ_STORYBOOK].oAnimWidth; + filesWidths[OFILE_CANDLE2] = AllObjects[OBJ_STORYCANDLE].oAnimWidth; } for (const ObjectData objectData : AllObjects) { @@ -4101,22 +4102,22 @@ void InitObjectGFX() continue; } - filesLoaded[objectData.ofindex] = true; + filesWidths[objectData.ofindex] = objectData.oAnimWidth; } if (objectData.otheme != THEME_NONE) { for (int j = 0; j < numthemes; j++) { if (themes[j].ttype == objectData.otheme) { - filesLoaded[objectData.ofindex] = true; + filesWidths[objectData.ofindex] = objectData.oAnimWidth; } } } if (objectData.oquest != Q_INVALID && Quests[objectData.oquest].IsAvailable()) { - filesLoaded[objectData.ofindex] = true; + filesWidths[objectData.ofindex] = objectData.oAnimWidth; } } - LoadLevelObjects(filesLoaded); + LoadLevelObjects(filesWidths); } void FreeObjectGFX() @@ -4347,7 +4348,7 @@ void InitObjects() void SetMapObjects(const uint16_t *dunData, int startx, int starty) { - bool filesLoaded[65] = {}; + uint16_t filesWidths[65] = {}; ClrAllObjects(); ApplyObjectLighting = true; @@ -4367,12 +4368,13 @@ void SetMapObjects(const uint16_t *dunData, int startx, int starty) for (int i = 0; i < width; i++) { auto objectId = static_cast(SDL_SwapLE16(objectLayer[j * width + i])); if (objectId != 0) { - filesLoaded[AllObjects[ObjTypeConv[objectId]].ofindex] = true; + const ObjectData &objectData = AllObjects[ObjTypeConv[objectId]]; + filesWidths[objectData.ofindex] = objectData.oAnimWidth; } } } - LoadLevelObjects(filesLoaded); + LoadLevelObjects(filesWidths); for (int j = 0; j < height; j++) { for (int i = 0; i < width; i++) { diff --git a/Source/panels/charpanel.cpp b/Source/panels/charpanel.cpp index 85581debd..26e8cc6f8 100644 --- a/Source/panels/charpanel.cpp +++ b/Source/panels/charpanel.cpp @@ -7,7 +7,7 @@ #include "DiabloUI/art.h" #include "DiabloUI/art_draw.h" #include "control.h" -#include "engine/render/cel_render.hpp" +#include "engine/render/cl2_render.hpp" #include "engine/render/text_render.hpp" #include "panels/ui_panels.hpp" #include "player.h" @@ -255,13 +255,13 @@ void DrawStatButtons(const Surface &out) if (MyPlayer->_pStatPts > 0) { CelSprite sprite { *pChrButtons }; if (MyPlayer->_pBaseStr < MyPlayer->GetMaximumAttributeValue(CharacterAttribute::Strength)) - CelDrawTo(out, GetPanelPosition(UiPanels::Character, { 137, 157 }), sprite, chrbtn[static_cast(CharacterAttribute::Strength)] ? 2 : 1); + Cl2Draw(out, GetPanelPosition(UiPanels::Character, { 137, 157 }), sprite, chrbtn[static_cast(CharacterAttribute::Strength)] ? 2 : 1); if (MyPlayer->_pBaseMag < MyPlayer->GetMaximumAttributeValue(CharacterAttribute::Magic)) - CelDrawTo(out, GetPanelPosition(UiPanels::Character, { 137, 185 }), sprite, chrbtn[static_cast(CharacterAttribute::Magic)] ? 4 : 3); + Cl2Draw(out, GetPanelPosition(UiPanels::Character, { 137, 185 }), sprite, chrbtn[static_cast(CharacterAttribute::Magic)] ? 4 : 3); if (MyPlayer->_pBaseDex < MyPlayer->GetMaximumAttributeValue(CharacterAttribute::Dexterity)) - CelDrawTo(out, GetPanelPosition(UiPanels::Character, { 137, 214 }), sprite, chrbtn[static_cast(CharacterAttribute::Dexterity)] ? 6 : 5); + Cl2Draw(out, GetPanelPosition(UiPanels::Character, { 137, 214 }), sprite, chrbtn[static_cast(CharacterAttribute::Dexterity)] ? 6 : 5); if (MyPlayer->_pBaseVit < MyPlayer->GetMaximumAttributeValue(CharacterAttribute::Vitality)) - CelDrawTo(out, GetPanelPosition(UiPanels::Character, { 137, 242 }), sprite, chrbtn[static_cast(CharacterAttribute::Vitality)] ? 8 : 7); + Cl2Draw(out, GetPanelPosition(UiPanels::Character, { 137, 242 }), sprite, chrbtn[static_cast(CharacterAttribute::Vitality)] ? 8 : 7); } } diff --git a/Source/panels/info_box.cpp b/Source/panels/info_box.cpp index 14a6c6929..9dc9ee5cf 100644 --- a/Source/panels/info_box.cpp +++ b/Source/panels/info_box.cpp @@ -9,8 +9,8 @@ OptionalOwnedCelSprite pSTextSlidCels; void InitInfoBoxGfx() { - pSTextSlidCels = LoadCel("Data\\TextSlid.CEL", 12); - pSTextBoxCels = LoadCel("Data\\TextBox2.CEL", 271); + pSTextSlidCels = LoadCelAsCl2("Data\\TextSlid.CEL", 12); + pSTextBoxCels = LoadCelAsCl2("Data\\TextBox2.CEL", 271); } void FreeInfoBoxGfx() diff --git a/Source/panels/mainpanel.cpp b/Source/panels/mainpanel.cpp index 1ea6ed488..61e8c6fb8 100644 --- a/Source/panels/mainpanel.cpp +++ b/Source/panels/mainpanel.cpp @@ -1,7 +1,7 @@ #include "panels/mainpanel.hpp" #include "control.h" -#include "engine/render/cel_render.hpp" +#include "engine/render/cl2_render.hpp" #include "engine/render/text_render.hpp" #include "utils/display.h" #include "utils/language.h" diff --git a/Source/panels/spell_book.cpp b/Source/panels/spell_book.cpp index 0db36e4fa..dbc317e3b 100644 --- a/Source/panels/spell_book.cpp +++ b/Source/panels/spell_book.cpp @@ -6,7 +6,7 @@ #include "engine/cel_sprite.hpp" #include "engine/load_cel.hpp" #include "engine/rectangle.hpp" -#include "engine/render/cel_render.hpp" +#include "engine/render/cl2_render.hpp" #include "engine/render/text_render.hpp" #include "init.h" #include "missiles.h" @@ -81,9 +81,9 @@ spell_type GetSBookTrans(spell_id ii, bool townok) void InitSpellBook() { - pSpellBkCel = LoadCel("Data\\SpellBk.CEL", static_cast(SidePanelSize.width)); - pSBkBtnCel = LoadCel("Data\\SpellBkB.CEL", gbIsHellfire ? 61 : 76); - pSBkIconCels = LoadCel("Data\\SpellI2.CEL", 37); + pSpellBkCel = LoadCelAsCl2("Data\\SpellBk.CEL", static_cast(SidePanelSize.width)); + pSBkBtnCel = LoadCelAsCl2("Data\\SpellBkB.CEL", gbIsHellfire ? 61 : 76); + pSBkIconCels = LoadCelAsCl2("Data\\SpellI2.CEL", 37); Player &player = *MyPlayer; if (player._pClass == HeroClass::Warrior) { @@ -110,16 +110,16 @@ void FreeSpellBook() void DrawSpellBook(const Surface &out) { - CelDrawTo(out, GetPanelPosition(UiPanels::Spell, { 0, 351 }), CelSprite { *pSpellBkCel }, 0); + Cl2Draw(out, GetPanelPosition(UiPanels::Spell, { 0, 351 }), CelSprite { *pSpellBkCel }, 0); if (gbIsHellfire && sbooktab < 5) { - CelDrawTo(out, GetPanelPosition(UiPanels::Spell, { 61 * sbooktab + 7, 348 }), CelSprite { *pSBkBtnCel }, sbooktab); + Cl2Draw(out, GetPanelPosition(UiPanels::Spell, { 61 * sbooktab + 7, 348 }), CelSprite { *pSBkBtnCel }, sbooktab); } else { // BUGFIX: rendering of page 3 and page 4 buttons are both off-by-one pixel (fixed). int sx = 76 * sbooktab + 7; if (sbooktab == 2 || sbooktab == 3) { sx++; } - CelDrawTo(out, GetPanelPosition(UiPanels::Spell, { sx, 348 }), CelSprite { *pSBkBtnCel }, sbooktab); + Cl2Draw(out, GetPanelPosition(UiPanels::Spell, { sx, 348 }), CelSprite { *pSBkBtnCel }, sbooktab); } Player &player = *MyPlayer; uint64_t spl = player._pMemSpells | player._pISpells | player._pAblSpells; diff --git a/Source/panels/spell_icons.cpp b/Source/panels/spell_icons.cpp index cbcaa26f0..d13f9a865 100644 --- a/Source/panels/spell_icons.cpp +++ b/Source/panels/spell_icons.cpp @@ -2,7 +2,7 @@ #include "engine/load_cel.hpp" #include "engine/palette.h" -#include "engine/render/cel_render.hpp" +#include "engine/render/cl2_render.hpp" #include "init.h" #include "utils/stdcompat/optional.hpp" @@ -71,9 +71,9 @@ const char SpellITbl[] = { void LoadSpellIcons() { if (!gbIsHellfire) - pSpellCels = LoadCel("CtrlPan\\SpelIcon.CEL", SPLICONLENGTH); + pSpellCels = LoadCelAsCl2("CtrlPan\\SpelIcon.CEL", SPLICONLENGTH); else - pSpellCels = LoadCel("Data\\SpelIcon.CEL", SPLICONLENGTH); + pSpellCels = LoadCelAsCl2("Data\\SpelIcon.CEL", SPLICONLENGTH); SetSpellTrans(RSPLTYPE_SKILL); } @@ -89,7 +89,7 @@ void DrawSpellCel(const Surface &out, Point position, int nCel) void DrawSpellCel(const Surface &out, Point position, const OwnedCelSprite &sprite, int nCel) { - CelDrawLightTo(out, position, CelSprite { sprite }, nCel, SplTransTbl); + Cl2DrawTRN(out, position, CelSprite { sprite }, nCel, SplTransTbl); } void SetSpellTrans(spell_type t) diff --git a/Source/qol/itemlabels.cpp b/Source/qol/itemlabels.cpp index 80ca24936..066fe365a 100644 --- a/Source/qol/itemlabels.cpp +++ b/Source/qol/itemlabels.cpp @@ -9,7 +9,7 @@ #include "control.h" #include "cursor.h" #include "engine/point.hpp" -#include "engine/render/cel_render.hpp" +#include "engine/render/cl2_render.hpp" #include "gmenu.h" #include "inv.h" #include "itemlabels.h" @@ -80,7 +80,7 @@ void AddItemToLabelQueue(int id, int x, int y) nameWidth += MarginX * 2; int index = ItemCAnimTbl[item._iCurs]; if (!labelCenterOffsets[index]) { - std::pair itemBounds = MeasureSolidHorizontalBounds(*item.AnimInfo.celSprite, item.AnimInfo.currentFrame); + std::pair itemBounds = Cl2MeasureSolidHorizontalBounds(*item.AnimInfo.celSprite, item.AnimInfo.currentFrame); labelCenterOffsets[index].emplace((itemBounds.first + itemBounds.second) / 2); } diff --git a/Source/qol/stash.cpp b/Source/qol/stash.cpp index 9459080ca..07d9d3628 100644 --- a/Source/qol/stash.cpp +++ b/Source/qol/stash.cpp @@ -10,7 +10,7 @@ #include "cursor.h" #include "engine/points_in_rectangle_range.hpp" #include "engine/rectangle.hpp" -#include "engine/render/cel_render.hpp" +#include "engine/render/cl2_render.hpp" #include "engine/render/text_render.hpp" #include "engine/size.hpp" #include "hwcursor.hpp" @@ -375,10 +375,10 @@ void DrawStash(const Surface &out) if (pcursstashitem == itemId) { uint8_t color = GetOutlineColor(item, true); - CelBlitOutlineTo(out, color, position, cel, celFrame, false); + Cl2DrawOutline(out, color, position, cel, celFrame); } - CelDrawItem(item, out, position, cel, celFrame); + DrawItem(item, out, position, cel, celFrame); } Point position = GetPanelPosition(UiPanels::Stash); @@ -621,7 +621,7 @@ void DrawGoldWithdraw(const Surface &out, int amount) const int dialogX = 30; - CelDrawTo(out, GetPanelPosition(UiPanels::Stash, { dialogX, 178 }), CelSprite { *pGBoxBuff }, 0); + Cl2Draw(out, GetPanelPosition(UiPanels::Stash, { dialogX, 178 }), CelSprite { *pGBoxBuff }, 0); // Pre-wrap the string at spaces, otherwise DrawString would hard wrap in the middle of words const std::string wrapped = WordWrapString(_("How many gold pieces do you want to withdraw?"), 200); diff --git a/Source/quests.cpp b/Source/quests.cpp index 75d39ec9f..feee92906 100644 --- a/Source/quests.cpp +++ b/Source/quests.cpp @@ -12,7 +12,7 @@ #include "cursor.h" #include "engine/load_file.hpp" #include "engine/random.hpp" -#include "engine/render/cel_render.hpp" +#include "engine/render/cl2_render.hpp" #include "engine/render/text_render.hpp" #include "init.h" #include "levels/gendung.h" @@ -215,11 +215,11 @@ void PrintQLString(const Surface &out, int x, int y, string_view str, bool marke int width = GetLineWidth(str); x += std::max((257 - width) / 2, 0); if (marked) { - CelDrawTo(out, GetPanelPosition(UiPanels::Quest, { x - 20, y + 13 }), CelSprite { *pSPentSpn2Cels }, PentSpn2Spin()); + Cl2Draw(out, GetPanelPosition(UiPanels::Quest, { x - 20, y + 13 }), CelSprite { *pSPentSpn2Cels }, PentSpn2Spin()); } DrawString(out, str, { GetPanelPosition(UiPanels::Quest, { x, y }), { 257, 0 } }, disabled ? UiFlags::ColorWhitegold : UiFlags::ColorWhite); if (marked) { - CelDrawTo(out, GetPanelPosition(UiPanels::Quest, { x + width + 7, y + 13 }), CelSprite { *pSPentSpn2Cels }, PentSpn2Spin()); + Cl2Draw(out, GetPanelPosition(UiPanels::Quest, { x + width + 7, y + 13 }), CelSprite { *pSPentSpn2Cels }, PentSpn2Spin()); } } @@ -676,7 +676,7 @@ void DrawQuestLog(const Surface &out) SelectedQuest = l; } const auto x = InnerPanel.position.x; - CelDrawTo(out, GetPanelPosition(UiPanels::Quest, { 0, 351 }), CelSprite { *pQLogCel }, 0); + Cl2Draw(out, GetPanelPosition(UiPanels::Quest, { 0, 351 }), CelSprite { *pQLogCel }, 0); int y = InnerPanel.position.y + ListYOffset; for (int i = 0; i < EncounteredQuestCount; i++) { if (i == FirstFinishedQuest) { diff --git a/Source/stores.cpp b/Source/stores.cpp index bb9efe231..20e2bc171 100644 --- a/Source/stores.cpp +++ b/Source/stores.cpp @@ -13,7 +13,7 @@ #include "cursor.h" #include "engine/load_cel.hpp" #include "engine/random.hpp" -#include "engine/render/cel_render.hpp" +#include "engine/render/cl2_render.hpp" #include "engine/render/text_render.hpp" #include "init.h" #include "minitext.h" @@ -196,7 +196,7 @@ void CalculateLineHeights() void DrawSTextBack(const Surface &out) { const Point uiPosition = GetUIRectangle().position; - CelDrawTo(out, { uiPosition.x + 320 + 24, 327 + uiPosition.y }, CelSprite { *pSTextBoxCels }, 0); + Cl2Draw(out, { uiPosition.x + 320 + 24, 327 + uiPosition.y }, CelSprite { *pSTextBoxCels }, 0); DrawHalfTransparentRectTo(out, uiPosition.x + 347, uiPosition.y + 28, 265, 297); } @@ -207,17 +207,17 @@ void DrawSSlider(const Surface &out, int y1, int y2) int yd2 = y2 * 12 + 44 + uiPosition.y; CelSprite sprite { *pSTextSlidCels }; if (stextscrlubtn != -1) - CelDrawTo(out, { uiPosition.x + 601, yd1 }, sprite, 11); + Cl2Draw(out, { uiPosition.x + 601, yd1 }, sprite, 11); else - CelDrawTo(out, { uiPosition.x + 601, yd1 }, sprite, 9); + Cl2Draw(out, { uiPosition.x + 601, yd1 }, sprite, 9); if (stextscrldbtn != -1) - CelDrawTo(out, { uiPosition.x + 601, yd2 }, sprite, 10); + Cl2Draw(out, { uiPosition.x + 601, yd2 }, sprite, 10); else - CelDrawTo(out, { uiPosition.x + 601, yd2 }, sprite, 8); + Cl2Draw(out, { uiPosition.x + 601, yd2 }, sprite, 8); yd1 += 12; int yd3 = yd1; for (; yd3 < yd2; yd3 += 12) { - CelDrawTo(out, { uiPosition.x + 601, yd3 }, sprite, 13); + Cl2Draw(out, { uiPosition.x + 601, yd3 }, sprite, 13); } if (stextsel == BackButtonLine()) yd3 = stextlhold; @@ -227,7 +227,7 @@ void DrawSSlider(const Surface &out, int y1, int y2) yd3 = 1000 * (stextsval + ((yd3 - stextup) / 4)) / (storenumh - 1) * (y2 * 12 - y1 * 12 - 24) / 1000; else yd3 = 0; - CelDrawTo(out, { uiPosition.x + 601, (y1 + 1) * 12 + 44 + uiPosition.y + yd3 }, sprite, 12); + Cl2Draw(out, { uiPosition.x + 601, (y1 + 1) * 12 + 44 + uiPosition.y + yd3 }, sprite, 12); } void AddSLine(size_t y) @@ -2174,13 +2174,13 @@ void DrawSelector(const Surface &out, const Rectangle &rect, string_view text, U if (HasAnyOf(flags, UiFlags::AlignCenter)) x1 += (rect.size.width - lineWidth) / 2; - CelDrawTo(out, { x1, rect.position.y + 13 }, CelSprite { *pSPentSpn2Cels }, PentSpn2Spin()); + Cl2Draw(out, { x1, rect.position.y + 13 }, CelSprite { *pSPentSpn2Cels }, PentSpn2Spin()); int x2 = rect.position.x + rect.size.width + 5; if (HasAnyOf(flags, UiFlags::AlignCenter)) x2 = rect.position.x + (rect.size.width - lineWidth) / 2 + lineWidth + 5; - CelDrawTo(out, { x2, rect.position.y + 13 }, CelSprite { *pSPentSpn2Cels }, PentSpn2Spin()); + Cl2Draw(out, { x2, rect.position.y + 13 }, CelSprite { *pSPentSpn2Cels }, PentSpn2Spin()); } } // namespace diff --git a/Source/towners.cpp b/Source/towners.cpp index 00ad41e4b..85efe5dbd 100644 --- a/Source/towners.cpp +++ b/Source/towners.cpp @@ -2,11 +2,13 @@ #include "cursor.h" #include "engine/cel_header.hpp" +#include "engine/load_cel.hpp" #include "engine/load_file.hpp" #include "engine/random.hpp" #include "inv.h" #include "minitext.h" #include "stores.h" +#include "utils/cel_to_cl2.hpp" #include "utils/language.h" namespace devilution { @@ -52,7 +54,7 @@ void InitTownerInfo(int i, const TownerData &townerData) void LoadTownerAnimations(Towner &towner, const char *path, int frames, int delay) { - towner.data = LoadFileInMem(path); + towner.data = LoadCelAsCl2(path, towner._tAnimWidth).data(); NewTownerAnim(towner, towner.data.get(), frames, delay); } @@ -818,7 +820,7 @@ void InitTowners() { assert(CowCels == nullptr); - CowCels = LoadFileInMem("Towners\\Animals\\Cow.CEL"); + CowCels = LoadCelAsCl2("Towners\\Animals\\Cow.CEL", 128).data(); int i = 0; for (const auto &townerData : TownersData) { diff --git a/Source/utils/cel_to_cl2.cpp b/Source/utils/cel_to_cl2.cpp new file mode 100644 index 000000000..97dfd474f --- /dev/null +++ b/Source/utils/cel_to_cl2.cpp @@ -0,0 +1,208 @@ +#include "utils/cel_to_cl2.hpp" + +#include + +#include + +#ifdef DEBUG_CEL_TO_CL2_SIZE +#include +#include +#endif + +#include "appfat.h" +#include "utils/endian.hpp" + +namespace devilution { + +namespace { + +constexpr bool IsCelTransparent(uint8_t control) +{ + constexpr uint8_t CelTransparentMin = 0x80; + return control >= CelTransparentMin; +} + +constexpr uint8_t GetCelTransparentWidth(uint8_t control) +{ + return -static_cast(control); +} + +void AppendCl2TransparentRun(unsigned width, std::vector &out) +{ + while (width >= 0x7F) { + out.push_back(0x7F); + width -= 0x7F; + } + if (width == 0) + return; + out.push_back(width); +} + +void AppendCl2FillRun(uint8_t color, unsigned width, std::vector &out) +{ + while (width >= 0x3F) { + out.push_back(0x80); + out.push_back(color); + width -= 0x3F; + } + if (width == 0) + return; + out.push_back(0xBF - width); + out.push_back(color); +} + +void AppendCl2PixelsRun(const uint8_t *src, unsigned width, std::vector &out) +{ + while (width >= 0x41) { + out.push_back(0xBF); + for (size_t i = 0; i < 0x41; ++i) + out.push_back(src[i]); + width -= 0x41; + src += 0x41; + } + if (width == 0) + return; + out.push_back(256 - width); + for (size_t i = 0; i < width; ++i) + out.push_back(src[i]); +} + +void AppendCl2PixelsOrFillRun(const uint8_t *src, unsigned length, std::vector &out) +{ + const uint8_t *begin = src; + const uint8_t *prevColorBegin = src; + unsigned prevColorRunLength = 1; + uint8_t prevColor = *src++; + while (--length > 0) { + const uint8_t color = *src; + if (prevColor == color) { + ++prevColorRunLength; + } else { + // A tunable parameter that decides at which minimum length we encode a fill run. + // 3 appears to be optimal for most of our data (much better than 2, rarely very slightly worse than 4). + constexpr unsigned MinFillRunLength = 3; + if (prevColorRunLength >= MinFillRunLength) { + AppendCl2PixelsRun(begin, prevColorBegin - begin, out); + AppendCl2FillRun(prevColor, prevColorRunLength, out); + begin = src; + } + prevColorBegin = src; + prevColorRunLength = 1; + prevColor = color; + } + ++src; + } + AppendCl2PixelsRun(begin, prevColorBegin - begin, out); + AppendCl2FillRun(prevColor, prevColorRunLength, out); +} + +} // namespace + +OwnedCelSprite CelToCl2(const uint8_t *data, size_t size, PointerOrValue widthOrWidths) +{ + // A CEL file either begins with: + // 1. A CEL header. + // 2. A list of offsets to frame groups (each group is a CEL file). + size_t groupsHeaderSize = 0; + uint32_t numGroups = 1; + const uint32_t maybeNumFrames = LoadLE32(data); + + std::vector cl2Data; + + // Most files become smaller with CL2. Allocate exactly enough bytes to avoid reallocation. + // The only file that becomes larger is Data\hf_logo3.CEL, by exactly 4445 bytes. + cl2Data.reserve(size + 4445); + + // If it is a number of frames, then the last frame offset will be equal to the size of the file. + if (LoadLE32(&data[maybeNumFrames * 4 + 4]) != size) { + // maybeNumFrames is the address of the first group, right after + // the list of group offsets. + numGroups = maybeNumFrames / 4; + groupsHeaderSize = maybeNumFrames; + data += groupsHeaderSize; + cl2Data.resize(groupsHeaderSize); + } + + for (size_t group = 0; group < numGroups; ++group) { + uint32_t numFrames; + if (numGroups == 1) { + numFrames = maybeNumFrames; + } else { + numFrames = LoadLE32(data); + WriteLE32(&cl2Data[4 * group], cl2Data.size()); + } + + // CL2 header: frame count, frame offset for each frame, file size + const size_t cl2DataOffset = cl2Data.size(); + cl2Data.resize(cl2Data.size() + 4 * (2 + static_cast(numFrames))); + WriteLE32(cl2Data.data(), numFrames); + + const uint8_t *srcEnd = &data[LoadLE32(&data[4])]; + for (size_t frame = 1; frame <= numFrames; ++frame) { + const uint8_t *src = srcEnd; + srcEnd = &data[LoadLE32(&data[4 * (frame + 1)])]; + WriteLE32(&cl2Data[cl2DataOffset + 4 * frame], static_cast(cl2Data.size() - cl2DataOffset)); + + // Skip CEL frame header if there is one. + constexpr size_t CelFrameHeaderSize = 10; + const bool celFrameHasHeader = LoadLE16(src) == CelFrameHeaderSize; + if (celFrameHasHeader) + src += CelFrameHeaderSize; + + const unsigned frameWidth = widthOrWidths.HoldsPointer() ? widthOrWidths.AsPointer()[frame - 1] : widthOrWidths.AsValue(); + + // Frame header: 5 16-bit offsets to 32-pixel height blocks. + const size_t frameHeaderPos = cl2Data.size(); + constexpr size_t FrameHeaderSize = 10; + cl2Data.resize(cl2Data.size() + FrameHeaderSize); + + // Frame header offset (first of five): + WriteLE16(&cl2Data[frameHeaderPos], FrameHeaderSize); + + unsigned transparentRunWidth = 0; + size_t line = 0; + while (src != srcEnd) { + // Process line: + for (unsigned remainingCelWidth = frameWidth; remainingCelWidth != 0;) { + uint8_t val = *src++; + if (IsCelTransparent(val)) { + val = GetCelTransparentWidth(val); + transparentRunWidth += val; + } else { + AppendCl2TransparentRun(transparentRunWidth, cl2Data); + transparentRunWidth = 0; + AppendCl2PixelsOrFillRun(src, val, cl2Data); + src += val; + } + remainingCelWidth -= val; + } + + // Frame header offset: + switch (++line) { + case 32: + case 64: + case 96: + case 128: + // Finish any active transparent run to not allow it to go over an offset line boundary. + AppendCl2TransparentRun(transparentRunWidth, cl2Data); + transparentRunWidth = 0; + WriteLE16(&cl2Data[frameHeaderPos + line / 16], static_cast(cl2Data.size() - frameHeaderPos)); + break; + } + } + AppendCl2TransparentRun(transparentRunWidth, cl2Data); + } + + WriteLE32(&cl2Data[cl2DataOffset + 4 * (1 + static_cast(numFrames))], static_cast(cl2Data.size() - cl2DataOffset)); + data = srcEnd; + } + + auto out = std::unique_ptr(new byte[cl2Data.size()]); + memcpy(&out[0], cl2Data.data(), cl2Data.size()); +#ifdef DEBUG_CEL_TO_CL2_SIZE + std::cout << "\t" << size << "\t" << cl2Data.size() << "\t" << std::setprecision(1) << std::fixed << (static_cast(cl2Data.size()) - static_cast(size)) / ((float)size) * 100 << "%" << std::endl; +#endif + return OwnedCelSprite { std::move(out), widthOrWidths }; +} + +} // namespace devilution diff --git a/Source/utils/cel_to_cl2.hpp b/Source/utils/cel_to_cl2.hpp new file mode 100644 index 000000000..8bfaaa3ed --- /dev/null +++ b/Source/utils/cel_to_cl2.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include +#include + +#include "engine/cel_sprite.hpp" +#include "utils/pointer_value_union.hpp" + +namespace devilution { + +OwnedCelSprite CelToCl2(const uint8_t *data, size_t size, PointerOrValue widthOrWidths); + +} // namespace devilution