diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 73c628293..55bc80f37 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -84,6 +84,7 @@ set(libdevilutionx_SRCS engine/demomode.cpp engine/direction.cpp engine/load_cel.cpp + engine/load_pcx_as_cel.cpp engine/random.cpp engine/render/automap_render.cpp engine/render/cel_render.cpp diff --git a/Source/engine/load_pcx_as_cel.cpp b/Source/engine/load_pcx_as_cel.cpp new file mode 100644 index 000000000..9670fb971 --- /dev/null +++ b/Source/engine/load_pcx_as_cel.cpp @@ -0,0 +1,21 @@ +#include "engine/load_pcx_as_cel.hpp" + +#include + +#include "engine/assets.hpp" +#include "utils/log.hpp" +#include "utils/pcx_to_cel.hpp" + +namespace devilution { + +std::optional LoadPcxAssetAsCel(const char *filename, unsigned numFrames, bool generateFrameHeaders, uint8_t transparentColorIndex) +{ + SDL_RWops *handle = OpenAsset(filename); + if (handle == nullptr) { + LogError("Missing file: {}", filename); + return std::nullopt; + } + return LoadPcxAsCel(handle, numFrames, generateFrameHeaders, transparentColorIndex); +} + +} // namespace devilution diff --git a/Source/engine/load_pcx_as_cel.hpp b/Source/engine/load_pcx_as_cel.hpp new file mode 100644 index 000000000..a3ae221b6 --- /dev/null +++ b/Source/engine/load_pcx_as_cel.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include + +#include "engine/cel_sprite.hpp" +#include "utils/stdcompat/optional.hpp" + +namespace devilution { + +std::optional LoadPcxAssetAsCel(const char *filename, unsigned numFrames, bool generateFrameHeaders = false, uint8_t transparentColorIndex = 1); + +} // namespace devilution diff --git a/Source/engine/render/text_render.cpp b/Source/engine/render/text_render.cpp index 1f5b741bc..387bf91db 100644 --- a/Source/engine/render/text_render.cpp +++ b/Source/engine/render/text_render.cpp @@ -17,11 +17,11 @@ #include "engine.h" #include "engine/load_cel.hpp" #include "engine/load_file.hpp" +#include "engine/load_pcx_as_cel.hpp" #include "engine/point.hpp" #include "palette.h" #include "utils/display.h" #include "utils/language.h" -#include "utils/pcx_to_cel.hpp" #include "utils/sdl_compat.h" #include "utils/stdcompat/optional.hpp" #include "utils/utf8.hpp" @@ -194,17 +194,10 @@ const OwnedCelSpriteWithFrameHeight *LoadFont(GameFontTables size, text_color co GetFontPath(size, row, &path[0]); std::optional &font = Fonts[fontId]; - SDL_RWops *handle = OpenAsset(path); - if (handle == nullptr) { - LogError("Missing font: {}", path); - return nullptr; - } - constexpr unsigned NumFrames = 256; - font = LoadPcxAsCel(handle, NumFrames, /*generateFrameHeaders=*/false); - if (font->sprite.Data() == nullptr) { + font = LoadPcxAssetAsCel(path, NumFrames); + if (!font) { LogError("Error loading font: {}", path); - font = std::nullopt; return nullptr; } diff --git a/Source/utils/pcx_to_cel.cpp b/Source/utils/pcx_to_cel.cpp index 4802a9b00..503982c99 100644 --- a/Source/utils/pcx_to_cel.cpp +++ b/Source/utils/pcx_to_cel.cpp @@ -18,8 +18,6 @@ namespace devilution { namespace { -constexpr uint8_t PcxTransparentColorIndex = 1; - void WriteLE32(uint8_t *out, uint32_t val) { const uint32_t littleEndian = SDL_SwapLE32(val); @@ -32,26 +30,40 @@ void WriteLE16(uint8_t *out, uint16_t val) memcpy(out, &littleEndian, 2); } -void AppendCelTransparentRun(uint8_t width, std::vector &out) +void AppendCelTransparentRun(unsigned width, std::vector &out) { + while (width >= 128) { + out.push_back(0x80); + width -= 128; + } + if (width == 0) + return; out.push_back(0xFF - (width - 1)); } void AppendCelSolidRun(const uint8_t *src, unsigned width, std::vector &out) { - assert(width < 126); + while (width >= 127) { + out.push_back(127); + for (size_t i = 0; i < 127; ++i) + out.push_back(src[i]); + width -= 127; + src += 127; + } + if (width == 0) + return; out.push_back(width); for (size_t i = 0; i < width; ++i) out.push_back(src[i]); } -void AppendCelLine(const uint8_t *src, unsigned width, std::vector &out) +void AppendCelLine(const uint8_t *src, unsigned width, uint8_t transparentColorIndex, std::vector &out) { unsigned runBegin = 0; bool transparentRun = false; for (unsigned i = 0; i < width; ++i) { const uint8_t pixel = src[i]; - if (pixel == PcxTransparentColorIndex) { + if (pixel == transparentColorIndex) { if (transparentRun) continue; if (runBegin != i) @@ -73,7 +85,7 @@ void AppendCelLine(const uint8_t *src, unsigned width, std::vector &out } // namespace -std::optional LoadPcxAsCel(SDL_RWops *handle, unsigned numFrames, bool generateFrameHeaders) +std::optional LoadPcxAsCel(SDL_RWops *handle, unsigned numFrames, bool generateFrameHeaders, uint8_t transparentColorIndex) { int width; int height; @@ -83,10 +95,9 @@ std::optional LoadPcxAsCel(SDL_RWops *handle, uns return std::nullopt; } assert(bpp == 8); - assert(width <= 128); - uint32_t pixelDataSize = SDL_RWsize(handle); - if (pixelDataSize == static_cast(-1)) { + ptrdiff_t pixelDataSize = SDL_RWsize(handle); + if (pixelDataSize < 0) { SDL_RWclose(handle); return std::nullopt; } @@ -141,20 +152,20 @@ std::optional LoadPcxAsCel(SDL_RWops *handle, uns size_t line = frameHeight; while (line-- != 0) { - AppendCelLine(&frameBuffer[line * width], width, celData); + AppendCelLine(&frameBuffer[line * width], width, transparentColorIndex, celData); if (generateFrameHeaders) { switch (line) { case 32: - WriteLE16(&celData[frameHeaderPos + 2], celData.size() - frameHeaderPos); + WriteLE16(&celData[frameHeaderPos + 2], static_cast(celData.size() - frameHeaderPos)); break; case 64: - WriteLE16(&celData[frameHeaderPos + 4], celData.size() - frameHeaderPos); + WriteLE16(&celData[frameHeaderPos + 4], static_cast(celData.size() - frameHeaderPos)); break; case 96: - WriteLE16(&celData[frameHeaderPos + 6], celData.size() - frameHeaderPos); + WriteLE16(&celData[frameHeaderPos + 6], static_cast(celData.size() - frameHeaderPos)); break; case 128: - WriteLE16(&celData[frameHeaderPos + 8], celData.size() - frameHeaderPos); + WriteLE16(&celData[frameHeaderPos + 8], static_cast(celData.size() - frameHeaderPos)); break; } } @@ -168,7 +179,7 @@ std::optional LoadPcxAsCel(SDL_RWops *handle, uns memcpy(&out[0], celData.data(), celData.size()); return OwnedCelSpriteWithFrameHeight { OwnedCelSprite { std::move(out), static_cast(width) }, - static_cast(frameHeight) + frameHeight }; } diff --git a/Source/utils/pcx_to_cel.hpp b/Source/utils/pcx_to_cel.hpp index 11e420032..a3528a4a3 100644 --- a/Source/utils/pcx_to_cel.hpp +++ b/Source/utils/pcx_to_cel.hpp @@ -1,5 +1,7 @@ #pragma once +#include + #include #include "engine/cel_sprite.hpp" @@ -14,7 +16,9 @@ namespace devilution { * @param handle A non-null SDL_RWops handle. Closed by this function. * @param numFrames The number of vertically stacked frames in the PCX file. * @param generateFrameHeaders Whether to generate frame headers in the CEL sprite. + * @param transparentColorIndex The PCX palette index of the transparent color. */ -std::optional LoadPcxAsCel(SDL_RWops *handle, unsigned numFrames, bool generateFrameHeaders); +std::optional LoadPcxAsCel(SDL_RWops *handle, unsigned numFrames, bool generateFrameHeaders, + uint8_t transparentColorIndex = 1); } // namespace devilution