Browse Source

♻️ Replace `SBmpLoadImage` with a simpler method

The new approach only makes 2 read calls and does not do any seeks.

It is also less general and is internal to `art.cpp`, allowing us to
simplify it somewhat.
pull/1814/head
Gleb Mazovetskiy 5 years ago committed by Anders Jenbo
parent
commit
3d03990cb8
  1. 135
      Source/DiabloUI/art.cpp
  2. 133
      Source/storm/storm.cpp
  3. 23
      Source/storm/storm.h

135
Source/DiabloUI/art.cpp

@ -1,10 +1,103 @@
#include "art.h"
#include <cstddef>
#include <cstdint>
#include <memory>
#include "DiabloUI/art.h"
#include "storm/storm.h"
#include "utils/display.h"
#include "utils/sdl_compat.h"
#include "utils/log.hpp"
#include "utils/sdl_compat.h"
namespace devilution {
namespace {
constexpr unsigned PcxHeaderSize = 128;
constexpr unsigned NumPaletteColors = 256;
constexpr unsigned PcxPaletteSize = 1 + NumPaletteColors * 3;
bool LoadPcxMeta(HANDLE handle, int &width, int &height, std::uint8_t &bpp)
{
PCXHeader pcxhdr;
if (!SFileReadFile(handle, &pcxhdr, PcxHeaderSize, nullptr, nullptr)) {
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(HANDLE handle, int width, int height, std::uint8_t bpp,
BYTE *buffer, std::size_t bufferPitch, SDL_Color *palette)
{
const bool has256ColorPalette = palette != nullptr && bpp == 8;
std::uint32_t pixelDataSize = SFileGetFileSize(handle, nullptr);
if (pixelDataSize == static_cast<std::uint32_t>(-1)) {
return false;
}
pixelDataSize -= PcxHeaderSize + (has256ColorPalette ? PcxPaletteSize : 0);
// We read 1 extra byte because it delimits the palette.
const unsigned readSize = pixelDataSize + (has256ColorPalette ? PcxPaletteSize : 0);
std::unique_ptr<BYTE[]> fileBuffer = std::make_unique<BYTE[]>(readSize);
if (!SFileReadFile(handle, fileBuffer.get(), readSize, nullptr, nullptr)) {
return false;
}
const unsigned xSkip = bufferPitch - width;
BYTE *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;
}
buffer += xSkip;
}
if (has256ColorPalette) {
[[maybe_unused]] constexpr unsigned PcxPaletteSeparator = 0x0C;
assert(*dataPtr == PcxPaletteSeparator);
++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)
{
switch (bpp) {
case 8: // NOLINT(readability-magic-numbers)
return SDL_PIXELFORMAT_INDEX8;
case 24: // NOLINT(readability-magic-numbers)
return SDL_PIXELFORMAT_RGB888;
case 32: // NOLINT(readability-magic-numbers)
return SDL_PIXELFORMAT_RGBA8888;
default:
return 0;
}
}
} // namespace
void LoadArt(const char *pszFile, Art *art, int frames, SDL_Color *pPalette)
{
@ -13,34 +106,28 @@ void LoadArt(const char *pszFile, Art *art, int frames, SDL_Color *pPalette)
art->frames = frames;
DWORD width, height, bpp;
if (!SBmpLoadImage(pszFile, nullptr, nullptr, 0, &width, &height, &bpp)) {
Log("Failed to load image meta");
HANDLE handle;
int width;
int height;
std::uint8_t bpp;
if (!SFileOpenFile(pszFile, &handle)) {
return;
}
Uint32 format;
switch (bpp) {
case 8:
format = SDL_PIXELFORMAT_INDEX8;
break;
case 24:
format = SDL_PIXELFORMAT_RGB888;
break;
case 32:
format = SDL_PIXELFORMAT_RGBA8888;
break;
default:
format = 0;
break;
if (!LoadPcxMeta(handle, width, height, bpp)) {
Log("LoadArt(\"{}\"): LoadPcxMeta failed with code {}", pszFile, SErrGetLastError());
SFileCloseFile(handle);
return;
}
SDLSurfaceUniquePtr artSurface { SDL_CreateRGBSurfaceWithFormat(SDL_SWSURFACE, width, height, bpp, format) };
if (!SBmpLoadImage(pszFile, pPalette, static_cast<BYTE *>(artSurface->pixels),
artSurface->pitch * artSurface->format->BytesPerPixel * height, nullptr, nullptr, nullptr)) {
Log("Failed to load image");
SDLSurfaceUniquePtr artSurface { SDL_CreateRGBSurfaceWithFormat(SDL_SWSURFACE, width, height, bpp, GetPcxSdlPixelFormat(bpp)) };
if (!LoadPcxPixelsAndPalette(handle, width, height, bpp, static_cast<BYTE *>(artSurface->pixels),
artSurface->pitch, pPalette)) {
Log("LoadArt(\"{}\"): LoadPcxPixelsAndPalette failed with code {}", pszFile, SErrGetLastError());
SFileCloseFile(handle);
return;
}
SFileCloseFile(handle);
art->logical_width = artSurface->w;
art->frame_height = height / frames;
@ -57,9 +144,11 @@ void LoadMaskedArt(const char *pszFile, Art *art, int frames, int mask)
void LoadArt(Art *art, const BYTE *artData, int w, int h, int frames)
{
constexpr int DefaultArtBpp = 8;
constexpr int DefaultArtFormat = SDL_PIXELFORMAT_INDEX8;
art->frames = frames;
art->surface = ScaleSurfaceToOutput(SDLSurfaceUniquePtr { SDL_CreateRGBSurfaceWithFormatFrom(
const_cast<BYTE *>(artData), w, h, 8, w, SDL_PIXELFORMAT_INDEX8) });
const_cast<BYTE *>(artData), w, h, DefaultArtBpp, w, DefaultArtFormat) });
art->logical_width = w;
art->frame_height = h / frames;
}

133
Source/storm/storm.cpp

@ -120,135 +120,14 @@ bool SFileOpenFile(const char *filename, HANDLE *phFile)
}
if (!result || (*phFile == nullptr)) {
Log("{}: Not found: {}", __FUNCTION__, filename);
}
return result;
}
bool SBmpLoadImage(const char *pszFileName, SDL_Color *pPalette, BYTE *pBuffer, DWORD dwBuffersize, DWORD *pdwWidth, DWORD *dwHeight, DWORD *pdwBpp)
{
HANDLE hFile;
size_t size;
PCXHeader pcxhdr;
BYTE paldata[256][3];
BYTE *dataPtr, *fileBuffer;
BYTE byte;
if (pdwWidth != nullptr)
*pdwWidth = 0;
if (dwHeight != nullptr)
*dwHeight = 0;
if (pdwBpp != nullptr)
*pdwBpp = 0;
if (!pszFileName || !*pszFileName) {
return false;
}
if (pBuffer && !dwBuffersize) {
return false;
}
if (!pPalette && !pBuffer && !pdwWidth && !dwHeight) {
return false;
}
if (!SFileOpenFile(pszFileName, &hFile)) {
return false;
}
while (strchr(pszFileName, 92) != nullptr)
pszFileName = strchr(pszFileName, 92) + 1;
while (strchr(pszFileName + 1, 46) != nullptr)
pszFileName = strchr(pszFileName, 46);
// omit all types except PCX
if (!pszFileName || strcasecmp(pszFileName, ".pcx") != 0) {
return false;
}
if (!SFileReadFile(hFile, &pcxhdr, 128, nullptr, nullptr)) {
SFileCloseFile(hFile);
return false;
}
int width = SDL_SwapLE16(pcxhdr.Xmax) - SDL_SwapLE16(pcxhdr.Xmin) + 1;
int height = SDL_SwapLE16(pcxhdr.Ymax) - SDL_SwapLE16(pcxhdr.Ymin) + 1;
// If the given buffer is larger than width * height, assume the extra data
// is scanline padding.
//
// This is useful because in SDL the pitch size is often slightly larger
// than image width for efficiency.
const int xSkip = dwBuffersize / height - width;
if (pdwWidth != nullptr)
*pdwWidth = width;
if (dwHeight != nullptr)
*dwHeight = height;
if (pdwBpp != nullptr)
*pdwBpp = pcxhdr.BitsPerPixel;
if (pBuffer == nullptr) {
SFileSetFilePointer(hFile, 0, nullptr, DVL_FILE_END);
fileBuffer = nullptr;
} else {
const auto pos = SFileGetFilePointer(hFile);
const auto end = SFileSetFilePointer(hFile, 0, DVL_FILE_END);
const auto begin = SFileSetFilePointer(hFile, pos, DVL_FILE_BEGIN);
size = end - begin;
fileBuffer = (BYTE *)malloc(size);
}
if (fileBuffer != nullptr) {
SFileReadFile(hFile, fileBuffer, size, nullptr, nullptr);
dataPtr = fileBuffer;
for (int j = 0; j < height; j++) {
for (int x = 0; x < width; dataPtr++) {
byte = *dataPtr;
if (byte < 0xC0) {
*pBuffer = byte;
pBuffer++;
x++;
continue;
}
dataPtr++;
for (int i = 0; i < (byte & 0x3F); i++) {
*pBuffer = *dataPtr;
pBuffer++;
x++;
}
}
// Skip the pitch padding.
pBuffer += xSkip;
const auto error = SErrGetLastError();
if (error == STORM_ERROR_FILE_NOT_FOUND) {
LogVerbose("{}(\"{}\") File not found", __FUNCTION__, filename);
} else {
LogError("{}(\"{}\") Failed with error code {}", __FUNCTION__, filename, error);
}
free(fileBuffer);
}
if (pPalette && pcxhdr.BitsPerPixel == 8) {
const auto pos = SFileSetFilePointer(hFile, -768, DVL_FILE_CURRENT);
if (pos == static_cast<std::uint64_t>(-1)) {
Log("SFileSetFilePointer error: {}", (unsigned int)SErrGetLastError());
}
SFileReadFile(hFile, paldata, 768, nullptr, nullptr);
for (int i = 0; i < 256; i++) {
pPalette[i].r = paldata[i][0];
pPalette[i].g = paldata[i][1];
pPalette[i].b = paldata[i][2];
#ifndef USE_SDL1
pPalette[i].a = SDL_ALPHA_OPAQUE;
#endif
}
}
SFileCloseFile(hFile);
return true;
return result;
}
bool getIniBool(const char *sectionName, const char *keyName, bool defaultValue)

23
Source/storm/storm.h

@ -252,29 +252,6 @@ DWORD WINAPI SFileGetFileSize(HANDLE hFile, uint32_t *lpFileSizeHigh);
DWORD WINAPI SFileSetFilePointer(HANDLE, int, int *, int);
bool WINAPI SFileCloseFile(HANDLE hFile);
/* SBmpLoadImage @ 323
*
* Load an image from an available archive into a buffer.
*
* pszFileName: The name of the graphic in an active archive.
* pPalette: An optional buffer that receives the image palette.
* pBuffer: A buffer that receives the image data.
* dwBuffersize: The size of the specified image buffer.
* pdwWidth: An optional variable that receives the image width.
* pdwHeight: An optional variable that receives the image height.
* pdwBpp: An optional variable that receives the image bits per pixel.
*
* Returns true if the image was supported and loaded correctly, false otherwise.
*/
bool SBmpLoadImage(
const char *pszFileName,
SDL_Color *pPalette,
BYTE *pBuffer,
DWORD dwBuffersize,
DWORD *pdwWidth,
DWORD *pdwHeight,
DWORD *pdwBpp);
bool getIniBool(const char *sectionName, const char *keyName, bool defaultValue = false);
float getIniFloat(const char *sectionName, const char *keyName, float defaultValue);
bool getIniValue(const char *sectionName, const char *keyName, char *string, int stringSize, const char *defaultString = "");

Loading…
Cancel
Save