diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index ab0ae3784..46e40a4d2 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -107,6 +107,7 @@ set(libdevilutionx_SRCS utils/language.cpp utils/logged_fstream.cpp utils/paths.cpp + utils/pcx.cpp utils/sdl_bilinear_scale.cpp utils/sdl_thread.cpp utils/utf8.cpp diff --git a/Source/DiabloUI/art.cpp b/Source/DiabloUI/art.cpp index 73770e5cd..71cb41aaf 100644 --- a/Source/DiabloUI/art.cpp +++ b/Source/DiabloUI/art.cpp @@ -13,78 +13,6 @@ namespace devilution { namespace { -constexpr size_t PcxHeaderSize = 128; -constexpr unsigned NumPaletteColors = 256; - -bool LoadPcxMeta(SDL_RWops *handle, int &width, int &height, std::uint8_t &bpp) -{ - PCXHeader pcxhdr; - if (SDL_RWread(handle, &pcxhdr, PcxHeaderSize, 1) == 0) { - return false; - } - width = SDL_SwapLE16(pcxhdr.Xmax) - SDL_SwapLE16(pcxhdr.Xmin) + 1; - height = SDL_SwapLE16(pcxhdr.Ymax) - SDL_SwapLE16(pcxhdr.Ymin) + 1; - bpp = pcxhdr.BitsPerPixel; - return true; -} - -bool LoadPcxPixelsAndPalette(SDL_RWops *handle, int width, int height, std::uint8_t bpp, - uint8_t *buffer, std::ptrdiff_t bufferPitch, SDL_Color *palette) -{ - std::ptrdiff_t pixelDataSize = SDL_RWsize(handle); - if (pixelDataSize < 0) { - // Unable to determine size, or an error occurred. - return false; - } - - // SDL_RWsize gives the total size of the file however we've already read the header from an earlier call to - // LoadPcxMeta, so we only need to read the remainder of the file. - const std::size_t readSize = pixelDataSize - PcxHeaderSize; - std::unique_ptr fileBuffer { new uint8_t[readSize] }; - if (SDL_RWread(handle, fileBuffer.get(), readSize, 1) == 0) { - return false; - } - const std::ptrdiff_t xSkip = bufferPitch - width; - const std::ptrdiff_t srcSkip = width % 2; - uint8_t *dataPtr = fileBuffer.get(); - for (int j = 0; j < height; j++) { - for (int x = 0; x < width;) { - constexpr std::uint8_t PcxMaxSinglePixel = 0xBF; - const std::uint8_t byte = *dataPtr++; - if (byte <= PcxMaxSinglePixel) { - *buffer++ = byte; - ++x; - continue; - } - constexpr std::uint8_t PcxRunLengthMask = 0x3F; - const std::uint8_t runLength = (byte & PcxRunLengthMask); - std::memset(buffer, *dataPtr++, runLength); - buffer += runLength; - x += runLength; - } - dataPtr += srcSkip; - buffer += xSkip; - } - - if (palette != nullptr && bpp == 8) { - // The file has a 256 color palette that needs to be loaded. - [[maybe_unused]] constexpr unsigned PcxPaletteSeparator = 0x0C; - assert(*dataPtr == PcxPaletteSeparator); // sanity check the delimiter - ++dataPtr; - - auto *out = palette; - for (unsigned i = 0; i < NumPaletteColors; ++i) { - out->r = *dataPtr++; - out->g = *dataPtr++; - out->b = *dataPtr++; -#ifndef USE_SDL1 - out->a = SDL_ALPHA_OPAQUE; -#endif - ++out; - } - } - return true; -} Uint32 GetPcxSdlPixelFormat(unsigned bpp) { diff --git a/Source/utils/pcx.cpp b/Source/utils/pcx.cpp new file mode 100644 index 000000000..dc5b39be7 --- /dev/null +++ b/Source/utils/pcx.cpp @@ -0,0 +1,88 @@ +#include "utils/pcx.hpp" + +#include +#include + +#include "appfat.h" + +#ifdef USE_SDL1 +#include "utils/sdl2_to_1_2_backports.h" +#endif + +namespace devilution { +namespace { +constexpr unsigned NumPaletteColors = 256; +constexpr unsigned PcxPaletteSize = 1 + NumPaletteColors * 3; +} // namespace + +bool LoadPcxMeta(SDL_RWops *handle, int &width, int &height, uint8_t &bpp) +{ + PCXHeader pcxhdr; + if (SDL_RWread(handle, &pcxhdr, PcxHeaderSize, 1) == 0) { + return false; + } + width = SDL_SwapLE16(pcxhdr.Xmax) - SDL_SwapLE16(pcxhdr.Xmin) + 1; + height = SDL_SwapLE16(pcxhdr.Ymax) - SDL_SwapLE16(pcxhdr.Ymin) + 1; + bpp = pcxhdr.BitsPerPixel; + return true; +} + +bool LoadPcxPixelsAndPalette(SDL_RWops *handle, int width, int height, std::uint8_t bpp, + uint8_t *buffer, std::ptrdiff_t bufferPitch, SDL_Color *palette) +{ + std::ptrdiff_t pixelDataSize = SDL_RWsize(handle); + if (pixelDataSize < 0) { + // Unable to determine size, or an error occurred. + return false; + } + + // SDL_RWsize gives the total size of the file however we've already read the header from an earlier call to + // LoadPcxMeta, so we only need to read the remainder of the file. + const std::size_t readSize = pixelDataSize - PcxHeaderSize; + std::unique_ptr fileBuffer { new uint8_t[readSize] }; + if (SDL_RWread(handle, fileBuffer.get(), readSize, 1) == 0) { + return false; + } + const std::ptrdiff_t xSkip = bufferPitch - width; + const std::ptrdiff_t srcSkip = width % 2; + uint8_t *dataPtr = fileBuffer.get(); + for (int j = 0; j < height; j++) { + for (int x = 0; x < width;) { + constexpr std::uint8_t PcxMaxSinglePixel = 0xBF; + const std::uint8_t byte = *dataPtr++; + if (byte <= PcxMaxSinglePixel) { + *buffer++ = byte; + ++x; + continue; + } + constexpr std::uint8_t PcxRunLengthMask = 0x3F; + const std::uint8_t runLength = (byte & PcxRunLengthMask); + std::memset(buffer, *dataPtr++, runLength); + buffer += runLength; + x += runLength; + } + dataPtr += srcSkip; + buffer += xSkip; + } + + if (palette != nullptr && bpp == 8) { + // The file has a 256 color palette that needs to be loaded. + [[maybe_unused]] constexpr unsigned PcxPaletteSeparator = 0x0C; + assert(*dataPtr == PcxPaletteSeparator); // sanity check the delimiter + ++dataPtr; + + auto *out = palette; + for (unsigned i = 0; i < NumPaletteColors; ++i) { + out->r = *dataPtr++; + out->g = *dataPtr++; + out->b = *dataPtr++; +#ifndef USE_SDL1 + out->a = SDL_ALPHA_OPAQUE; +#endif + ++out; + } + } + return true; +} + +} // namespace devilution diff --git a/Source/utils/pcx.hpp b/Source/utils/pcx.hpp index 37d6ed93a..0d7eae89f 100644 --- a/Source/utils/pcx.hpp +++ b/Source/utils/pcx.hpp @@ -1,7 +1,10 @@ #pragma once +#include #include +#include + namespace devilution { struct PCXHeader { @@ -25,4 +28,10 @@ struct PCXHeader { uint8_t Filler[54]; }; +static constexpr size_t PcxHeaderSize = 128; + +bool LoadPcxMeta(SDL_RWops *handle, int &width, int &height, uint8_t &bpp); +bool LoadPcxPixelsAndPalette(SDL_RWops *handle, int width, int height, std::uint8_t bpp, + uint8_t *buffer, std::ptrdiff_t bufferPitch, SDL_Color *palette); + } // namespace devilution