diff --git a/CMakeLists.txt b/CMakeLists.txt index 2c43c9a57..03dc80d54 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -352,6 +352,7 @@ set(devilutionx_SRCS Source/engine/animationinfo.cpp Source/engine/render/automap_render.cpp Source/engine/render/cel_render.cpp + Source/engine/render/cl2_render.cpp Source/engine/render/dun_render.cpp Source/qol/autopickup.cpp Source/qol/common.cpp diff --git a/Source/engine.cpp b/Source/engine.cpp index df7d60f2c..ccd3bf5f8 100644 --- a/Source/engine.cpp +++ b/Source/engine.cpp @@ -36,10 +36,6 @@ const uint32_t RndInc = 1; */ const uint32_t RndMult = 0x015A4E35; -namespace { -constexpr std::uint8_t MaxCl2Width = 65; -} // namespace - CelSprite LoadCel(const char *pszName, int width) { return CelSprite(LoadFileInMem(pszName), width); @@ -251,319 +247,6 @@ void LoadFileData(const char *pszName, byte *buffer, size_t fileLen) SFileCloseFile(file); } -/** - * @brief Apply the color swaps to a CL2 sprite - * @param p CL2 buffer - * @param ttbl Palette translation table - * @param nCel Frame number in CL2 file - */ -void Cl2ApplyTrans(byte *p, const std::array &ttbl, int nCel) -{ - assert(p != nullptr); - - for (int i = 1; i <= nCel; i++) { - int nDataSize; - byte *dst = CelGetFrame(p, i, &nDataSize) + 10; - nDataSize -= 10; - while (nDataSize > 0) { - auto width = static_cast(*dst++); - nDataSize--; - assert(nDataSize >= 0); - if (width < 0) { - width = -width; - if (width > MaxCl2Width) { - nDataSize--; - assert(nDataSize >= 0); - *dst = static_cast(ttbl[static_cast(*dst)]); - dst++; - } else { - nDataSize -= width; - assert(nDataSize >= 0); - for (; width > 0; width--) { - *dst = static_cast(ttbl[static_cast(*dst)]); - dst++; - } - } - } - } - } -} - -/** - * @brief Blit CL2 sprite to the given buffer - * @param out Target buffer - * @param sx Target buffer coordinate - * @param sy Target buffer coordinate - * @param pRLEBytes CL2 pixel stream (run-length encoded) - * @param nDataSize Size of CL2 in bytes - * @param nWidth Width of sprite - */ -static void Cl2BlitSafe(const CelOutputBuffer &out, int sx, int sy, const byte *pRLEBytes, int nDataSize, int nWidth) -{ - const byte *src = pRLEBytes; - BYTE *dst = out.at(sx, sy); - int w = nWidth; - - while (nDataSize > 0) { - auto width = static_cast(*src++); - nDataSize--; - if (width < 0) { - width = -width; - if (width > MaxCl2Width) { - width -= MaxCl2Width; - nDataSize--; - const auto fill = static_cast(*src++); - if (dst < out.end() && dst > out.begin()) { - w -= width; - while (width > 0) { - *dst = fill; - dst++; - width--; - } - if (w == 0) { - w = nWidth; - dst -= out.pitch() + w; - } - continue; - } - } else { - nDataSize -= width; - if (dst < out.end() && dst > out.begin()) { - w -= width; - while (width > 0) { - *dst = static_cast(*src); - src++; - dst++; - width--; - } - if (w == 0) { - w = nWidth; - dst -= out.pitch() + w; - } - continue; - } - src += width; - } - } - while (width > 0) { - if (width > w) { - dst += w; - width -= w; - w = 0; - } else { - dst += width; - w -= width; - width = 0; - } - if (w == 0) { - w = nWidth; - dst -= out.pitch() + w; - } - } - } -} - -/** - * @brief Blit a solid colder shape one pixel larger then the given sprite shape, to the given buffer - * @param out Target buffer - * @param sx Target buffer coordinate - * @param sy Target buffer coordinate - * @param pRLEBytes CL2 pixel stream (run-length encoded) - * @param nDataSize Size of CL2 in bytes - * @param nWidth Width of sprite - * @param col Color index from current palette - */ -static void Cl2BlitOutlineSafe(const CelOutputBuffer &out, int sx, int sy, const byte *pRLEBytes, int nDataSize, int nWidth, uint8_t col) -{ - const byte *src = pRLEBytes; - BYTE *dst = out.at(sx, sy); - int w = nWidth; - - while (nDataSize > 0) { - auto width = static_cast(*src++); - nDataSize--; - if (width < 0) { - width = -width; - if (width > MaxCl2Width) { - width -= MaxCl2Width; - nDataSize--; - if (static_cast(*src++) != 0 && dst < out.end() && dst > out.begin()) { - w -= width; - dst[-1] = col; - dst[width] = col; - while (width > 0) { - dst[-out.pitch()] = col; - dst[out.pitch()] = col; - dst++; - width--; - } - if (w == 0) { - w = nWidth; - dst -= out.pitch() + w; - } - continue; - } - } else { - nDataSize -= width; - if (dst < out.end() && dst > out.begin()) { - w -= width; - while (width > 0) { - if (static_cast(*src) != 0) { - dst[-1] = col; - dst[1] = col; - dst[-out.pitch()] = col; - // BUGFIX: only set `if (dst+out.pitch() < out.end())` - dst[out.pitch()] = col; - } - src++; - dst++; - width--; - } - if (w == 0) { - w = nWidth; - dst -= out.pitch() + w; - } - continue; - } - src += width; - } - } - while (width > 0) { - if (width > w) { - dst += w; - width -= w; - w = 0; - } else { - dst += width; - w -= width; - width = 0; - } - if (w == 0) { - w = nWidth; - dst -= out.pitch() + w; - } - } - } -} - -/** - * @brief Blit CL2 sprite, and apply lighting, to the given buffer - * @param out Target buffer - * @param sx Target buffer coordinate - * @param sy Target buffer coordinate - * @param pRLEBytes CL2 pixel stream (run-length encoded) - * @param nDataSize Size of CL2 in bytes - * @param nWidth With of CL2 sprite - * @param pTable Light color table - */ -static void Cl2BlitLightSafe(const CelOutputBuffer &out, int sx, int sy, const byte *pRLEBytes, int nDataSize, int nWidth, uint8_t *pTable) -{ - const byte *src = pRLEBytes; - BYTE *dst = out.at(sx, sy); - int w = nWidth; - - while (nDataSize > 0) { - auto width = static_cast(*src++); - nDataSize--; - if (width < 0) { - width = -width; - if (width > MaxCl2Width) { - width -= MaxCl2Width; - nDataSize--; - const uint8_t fill = pTable[static_cast(*src++)]; - if (dst < out.end() && dst > out.begin()) { - w -= width; - while (width > 0) { - *dst = fill; - dst++; - width--; - } - if (w == 0) { - w = nWidth; - dst -= out.pitch() + w; - } - continue; - } - } else { - nDataSize -= width; - if (dst < out.end() && dst > out.begin()) { - w -= width; - while (width > 0) { - *dst = pTable[static_cast(*src)]; - src++; - dst++; - width--; - } - if (w == 0) { - w = nWidth; - dst -= out.pitch() + w; - } - continue; - } - src += width; - } - } - while (width > 0) { - if (width > w) { - dst += w; - width -= w; - w = 0; - } else { - dst += width; - w -= width; - width = 0; - } - if (w == 0) { - w = nWidth; - dst -= out.pitch() + w; - } - } - } -} - -void Cl2Draw(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame) -{ - assert(frame > 0); - - int nDataSize; - const byte *pRLEBytes = CelGetFrameClipped(cel.Data(), frame, &nDataSize); - - Cl2BlitSafe(out, sx, sy, pRLEBytes, nDataSize, cel.Width(frame)); -} - -void Cl2DrawOutline(const CelOutputBuffer &out, uint8_t col, int sx, int sy, const CelSprite &cel, int frame) -{ - assert(frame > 0); - - int nDataSize; - const byte *pRLEBytes = CelGetFrameClipped(cel.Data(), frame, &nDataSize); - - const CelOutputBuffer &sub = out.subregionY(0, out.h() - 1); - Cl2BlitOutlineSafe(sub, sx, sy, pRLEBytes, nDataSize, cel.Width(frame), col); -} - -void Cl2DrawLightTbl(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame, char light) -{ - assert(frame > 0); - - int nDataSize; - const byte *pRLEBytes = CelGetFrameClipped(cel.Data(), frame, &nDataSize); - Cl2BlitLightSafe(out, sx, sy, pRLEBytes, nDataSize, cel.Width(frame), GetLightTable(light)); -} - -void Cl2DrawLight(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame) -{ - assert(frame > 0); - - int nDataSize; - const byte *pRLEBytes = CelGetFrameClipped(cel.Data(), frame, &nDataSize); - - if (light_table_index != 0) - Cl2BlitLightSafe(out, sx, sy, pRLEBytes, nDataSize, cel.Width(frame), &pLightTbl[light_table_index * 256]); - else - Cl2BlitSafe(out, sx, sy, pRLEBytes, nDataSize, cel.Width(frame)); -} - /** * @brief Fade to black and play a video * @param pszMovie file path of movie diff --git a/Source/engine.h b/Source/engine.h index b22fc2a79..3e1b7af16 100644 --- a/Source/engine.h +++ b/Source/engine.h @@ -408,48 +408,6 @@ inline void SetPixel(const CelOutputBuffer &out, Point position, std::uint8_t co out[position] = col; } -/** - * @brief Blit CL2 sprite, to the back buffer at the given coordianates - * @param out Output buffer - * @param sx Output buffer coordinate - * @param sy Output buffer coordinate - * @param pCelBuff CL2 buffer - * @param nCel CL2 frame number - */ -void Cl2Draw(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame); - -/** - * @brief Blit a solid colder shape one pixel larger then the given sprite shape, to the given buffer at the given coordianates - * @param col Color index from current palette - * @param out Output buffer - * @param sx Output buffer coordinate - * @param sy Output buffer coordinate - * @param pCelBuff CL2 buffer - * @param nCel CL2 frame number - */ -void Cl2DrawOutline(const CelOutputBuffer &out, uint8_t col, int sx, int sy, const CelSprite &cel, int frame); - -/** - * @brief Blit CL2 sprite, and apply a given lighting, to the given buffer at the given coordianates - * @param out Output buffer - * @param sx Output buffer coordinate - * @param sy Output buffer coordinate - * @param pCelBuff CL2 buffer - * @param nCel CL2 frame number - * @param light Light shade to use - */ -void Cl2DrawLightTbl(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame, char light); - -/** - * @brief Blit CL2 sprite, and apply lighting, to the given buffer at the given coordinates - * @param out Output buffer - * @param sx Output buffer coordinate - * @param sy Output buffer coordinate - * @param pCelBuff CL2 buffer - * @param nCel CL2 frame number - */ -void Cl2DrawLight(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame); - /** * @brief Draw a horizontal line segment in the target buffer (left to right) * @param out Target buffer @@ -549,7 +507,6 @@ std::unique_ptr LoadFileInMem(const char *path, size_t *elements = nullptr) return buf; } -void Cl2ApplyTrans(byte *p, const std::array &ttbl, int nCel); void PlayInGameMovie(const char *pszMovie); } // namespace devilution diff --git a/Source/engine/render/cl2_render.cpp b/Source/engine/render/cl2_render.cpp new file mode 100644 index 000000000..6b05de6de --- /dev/null +++ b/Source/engine/render/cl2_render.cpp @@ -0,0 +1,321 @@ + +#include "cl2_render.hpp" + +#include "engine/render/common_impl.h" +#include "scrollrt.h" + +namespace devilution { +namespace { + +constexpr std::uint8_t MaxCl2Width = 65; + +/** + * @brief Blit CL2 sprite to the given buffer + * @param out Target buffer + * @param sx Target buffer coordinate + * @param sy Target buffer coordinate + * @param pRLEBytes CL2 pixel stream (run-length encoded) + * @param nDataSize Size of CL2 in bytes + * @param nWidth Width of sprite + */ +void Cl2BlitSafe(const CelOutputBuffer &out, int sx, int sy, const byte *pRLEBytes, int nDataSize, int nWidth) +{ + const byte *src = pRLEBytes; + BYTE *dst = out.at(sx, sy); + int w = nWidth; + + while (nDataSize > 0) { + auto width = static_cast(*src++); + nDataSize--; + if (width < 0) { + width = -width; + if (width > MaxCl2Width) { + width -= MaxCl2Width; + nDataSize--; + const auto fill = static_cast(*src++); + if (dst < out.end() && dst > out.begin()) { + w -= width; + while (width > 0) { + *dst = fill; + dst++; + width--; + } + if (w == 0) { + w = nWidth; + dst -= out.pitch() + w; + } + continue; + } + } else { + nDataSize -= width; + if (dst < out.end() && dst > out.begin()) { + w -= width; + while (width > 0) { + *dst = static_cast(*src); + src++; + dst++; + width--; + } + if (w == 0) { + w = nWidth; + dst -= out.pitch() + w; + } + continue; + } + src += width; + } + } + while (width > 0) { + if (width > w) { + dst += w; + width -= w; + w = 0; + } else { + dst += width; + w -= width; + width = 0; + } + if (w == 0) { + w = nWidth; + dst -= out.pitch() + w; + } + } + } +} + +/** + * @brief Blit a solid colder shape one pixel larger then the given sprite shape, to the given buffer + * @param out Target buffer + * @param sx Target buffer coordinate + * @param sy Target buffer coordinate + * @param pRLEBytes CL2 pixel stream (run-length encoded) + * @param nDataSize Size of CL2 in bytes + * @param nWidth Width of sprite + * @param col Color index from current palette + */ +void Cl2BlitOutlineSafe(const CelOutputBuffer &out, int sx, int sy, const byte *pRLEBytes, int nDataSize, int nWidth, uint8_t col) +{ + const byte *src = pRLEBytes; + BYTE *dst = out.at(sx, sy); + int w = nWidth; + + while (nDataSize > 0) { + auto width = static_cast(*src++); + nDataSize--; + if (width < 0) { + width = -width; + if (width > MaxCl2Width) { + width -= MaxCl2Width; + nDataSize--; + if (static_cast(*src++) != 0 && dst < out.end() && dst > out.begin()) { + w -= width; + dst[-1] = col; + dst[width] = col; + while (width > 0) { + dst[-out.pitch()] = col; + dst[out.pitch()] = col; + dst++; + width--; + } + if (w == 0) { + w = nWidth; + dst -= out.pitch() + w; + } + continue; + } + } else { + nDataSize -= width; + if (dst < out.end() && dst > out.begin()) { + w -= width; + while (width > 0) { + if (static_cast(*src) != 0) { + dst[-1] = col; + dst[1] = col; + dst[-out.pitch()] = col; + // BUGFIX: only set `if (dst+out.pitch() < out.end())` + dst[out.pitch()] = col; + } + src++; + dst++; + width--; + } + if (w == 0) { + w = nWidth; + dst -= out.pitch() + w; + } + continue; + } + src += width; + } + } + while (width > 0) { + if (width > w) { + dst += w; + width -= w; + w = 0; + } else { + dst += width; + w -= width; + width = 0; + } + if (w == 0) { + w = nWidth; + dst -= out.pitch() + w; + } + } + } +} + +/** + * @brief Blit CL2 sprite, and apply lighting, to the given buffer + * @param out Target buffer + * @param sx Target buffer coordinate + * @param sy Target buffer coordinate + * @param pRLEBytes CL2 pixel stream (run-length encoded) + * @param nDataSize Size of CL2 in bytes + * @param nWidth With of CL2 sprite + * @param pTable Light color table + */ +void Cl2BlitLightSafe(const CelOutputBuffer &out, int sx, int sy, const byte *pRLEBytes, int nDataSize, int nWidth, uint8_t *pTable) +{ + const byte *src = pRLEBytes; + BYTE *dst = out.at(sx, sy); + int w = nWidth; + + while (nDataSize > 0) { + auto width = static_cast(*src++); + nDataSize--; + if (width < 0) { + width = -width; + if (width > MaxCl2Width) { + width -= MaxCl2Width; + nDataSize--; + const uint8_t fill = pTable[static_cast(*src++)]; + if (dst < out.end() && dst > out.begin()) { + w -= width; + while (width > 0) { + *dst = fill; + dst++; + width--; + } + if (w == 0) { + w = nWidth; + dst -= out.pitch() + w; + } + continue; + } + } else { + nDataSize -= width; + if (dst < out.end() && dst > out.begin()) { + w -= width; + while (width > 0) { + *dst = pTable[static_cast(*src)]; + src++; + dst++; + width--; + } + if (w == 0) { + w = nWidth; + dst -= out.pitch() + w; + } + continue; + } + src += width; + } + } + while (width > 0) { + if (width > w) { + dst += w; + width -= w; + w = 0; + } else { + dst += width; + w -= width; + width = 0; + } + if (w == 0) { + w = nWidth; + dst -= out.pitch() + w; + } + } + } +} + +} // namespace + +void Cl2ApplyTrans(byte *p, const std::array &ttbl, int nCel) +{ + assert(p != nullptr); + + for (int i = 1; i <= nCel; i++) { + int nDataSize; + byte *dst = CelGetFrame(p, i, &nDataSize) + 10; + nDataSize -= 10; + while (nDataSize > 0) { + auto width = static_cast(*dst++); + nDataSize--; + assert(nDataSize >= 0); + if (width < 0) { + width = -width; + if (width > MaxCl2Width) { + nDataSize--; + assert(nDataSize >= 0); + *dst = static_cast(ttbl[static_cast(*dst)]); + dst++; + } else { + nDataSize -= width; + assert(nDataSize >= 0); + for (; width > 0; width--) { + *dst = static_cast(ttbl[static_cast(*dst)]); + dst++; + } + } + } + } + } +} + +void Cl2Draw(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame) +{ + assert(frame > 0); + + int nDataSize; + const byte *pRLEBytes = CelGetFrameClipped(cel.Data(), frame, &nDataSize); + + Cl2BlitSafe(out, sx, sy, pRLEBytes, nDataSize, cel.Width(frame)); +} + +void Cl2DrawOutline(const CelOutputBuffer &out, uint8_t col, int sx, int sy, const CelSprite &cel, int frame) +{ + assert(frame > 0); + + int nDataSize; + const byte *pRLEBytes = CelGetFrameClipped(cel.Data(), frame, &nDataSize); + + const CelOutputBuffer &sub = out.subregionY(0, out.h() - 1); + Cl2BlitOutlineSafe(sub, sx, sy, pRLEBytes, nDataSize, cel.Width(frame), col); +} + +void Cl2DrawLightTbl(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame, char light) +{ + assert(frame > 0); + + int nDataSize; + const byte *pRLEBytes = CelGetFrameClipped(cel.Data(), frame, &nDataSize); + Cl2BlitLightSafe(out, sx, sy, pRLEBytes, nDataSize, cel.Width(frame), GetLightTable(light)); +} + +void Cl2DrawLight(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame) +{ + assert(frame > 0); + + int nDataSize; + const byte *pRLEBytes = CelGetFrameClipped(cel.Data(), frame, &nDataSize); + + if (light_table_index != 0) + Cl2BlitLightSafe(out, sx, sy, pRLEBytes, nDataSize, cel.Width(frame), &pLightTbl[light_table_index * 256]); + else + Cl2BlitSafe(out, sx, sy, pRLEBytes, nDataSize, cel.Width(frame)); +} + +} // namespace devilution diff --git a/Source/engine/render/cl2_render.hpp b/Source/engine/render/cl2_render.hpp new file mode 100644 index 000000000..60513d3bf --- /dev/null +++ b/Source/engine/render/cl2_render.hpp @@ -0,0 +1,65 @@ +/** + * @file cl2_render.hpp + * + * CL2 rendering. + */ +#pragma once + +#include +#include + +#include "engine.h" + +namespace devilution { + +/** + * @brief Apply the color swaps to a CL2 sprite + * @param p CL2 buffer + * @param ttbl Palette translation table + * @param nCel Frame number in CL2 file + */ +void Cl2ApplyTrans(byte *p, const std::array &ttbl, int nCel); + +/** + * @brief Blit CL2 sprite, to the back buffer at the given coordianates + * @param out Output buffer + * @param sx Output buffer coordinate + * @param sy Output buffer coordinate + * @param pCelBuff CL2 buffer + * @param nCel CL2 frame number + */ +void Cl2Draw(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame); + +/** + * @brief Blit a solid colder shape one pixel larger then the given sprite shape, to the given buffer at the given coordianates + * @param col Color index from current palette + * @param out Output buffer + * @param sx Output buffer coordinate + * @param sy Output buffer coordinate + * @param pCelBuff CL2 buffer + * @param nCel CL2 frame number + */ +void Cl2DrawOutline(const CelOutputBuffer &out, uint8_t col, int sx, int sy, const CelSprite &cel, int frame); + +/** + * @brief Blit CL2 sprite, and apply a given lighting, to the given buffer at the given coordianates + * @param out Output buffer + * @param sx Output buffer coordinate + * @param sy Output buffer coordinate + * @param pCelBuff CL2 buffer + * @param nCel CL2 frame number + * @param light Light shade to use + */ +void Cl2DrawLightTbl(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame, char light); + +/** + * @brief Blit CL2 sprite, and apply lighting, to the given buffer at the given coordinates + * @param out Output buffer + * @param sx Output buffer coordinate + * @param sy Output buffer coordinate + * @param pCelBuff CL2 buffer + * @param nCel CL2 frame number + */ +void Cl2DrawLight(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame); + +} // namespace devilution diff --git a/Source/monster.cpp b/Source/monster.cpp index 287b69f4e..4b7a0eef2 100644 --- a/Source/monster.cpp +++ b/Source/monster.cpp @@ -12,11 +12,9 @@ #include "control.h" #include "cursor.h" #include "dead.h" -#ifdef _DEBUG -#include "debug.h" -#endif #include "drlg_l1.h" #include "drlg_l4.h" +#include "engine/render/cl2_render.hpp" #include "init.h" #include "lighting.h" #include "minitext.h" @@ -29,6 +27,10 @@ #include "trigs.h" #include "utils/language.h" +#ifdef _DEBUG +#include "debug.h" +#endif + namespace devilution { #define NIGHTMARE_TO_HIT_BONUS 85 diff --git a/Source/scrollrt.cpp b/Source/scrollrt.cpp index f2e9dfccd..fb72ab961 100644 --- a/Source/scrollrt.cpp +++ b/Source/scrollrt.cpp @@ -11,6 +11,7 @@ #include "doom.h" #include "dx.h" #include "engine/render/cel_render.hpp" +#include "engine/render/cl2_render.hpp" #include "engine/render/dun_render.hpp" #include "error.h" #include "gmenu.h"