rodecomp/Source/Render/SurfaceDX9.cpp
2026-10-24 00:35:19 +00:00

238 lines
7.1 KiB
C++

#include "Render/SurfaceDX9.h"
#include <algorithm>
namespace {
// OFFSET: 0x00570970
// FUNCTION: fn_02430
RECT Clip(const RECT& rectangle, const D3DSURFACE_DESC& description) {
return {
std::clamp<LONG>(rectangle.left, 0, description.Width), std::clamp<LONG>(rectangle.top, 0, description.Height),
std::clamp<LONG>(rectangle.right, 0, description.Width), std::clamp<LONG>(rectangle.bottom, 0, description.Height)
};
}
// OFFSET: 0x00570C70
// FUNCTION: fn_02437
int BitsPerPixel(D3DFORMAT format) {
switch (format) {
case D3DFMT_A8R8G8B8:
case D3DFMT_X8R8G8B8:
return 32;
case D3DFMT_R5G6B5:
case D3DFMT_X1R5G5B5:
case D3DFMT_A1R5G5B5:
case D3DFMT_A4R4G4B4:
return 16;
default:
return 0;
}
}
// OFFSET: 0x00570D70
// FUNCTION: fn_02441
std::uint16_t Color1555(std::uint32_t color) {
return ((color >> 16) & 0x8000) | ((color & 255) >> 3) | ((color >> 6) & 0x3E0) | ((color >> 9) & 0x7C00);
}
UINT AlignDimension(int value) {
const std::uint32_t dimension = value;
return dimension + ((0u - dimension) & 3);
}
}
// OFFSET: 0x00570720
// FUNCTION: fn_02427
CSurfaceDX9::CSurfaceDX9(SurfaceDX9Context& context, int width, int height)
: m_context(&context), m_width(AlignDimension(width)), m_height(AlignDimension(height)), m_surface(nullptr) {
context.device->CreateOffscreenPlainSurface(
this->m_width, this->m_height, context.backBufferDescription.Format, D3DPOOL_DEFAULT, &this->m_surface, nullptr
);
}
// OFFSET: 0x005707D0
// FUNCTION: fn_02428
CSurfaceDX9::CSurfaceDX9(SurfaceDX9Context& context, IDirect3DSurface9* surface) : m_context(&context) {
D3DSURFACE_DESC description;
if (FAILED(surface->GetDesc(&description))) {
this->m_width = 0;
this->m_height = 0;
this->m_surface = nullptr;
} else {
this->m_width = description.Width;
this->m_height = description.Height;
this->m_surface = surface;
surface->AddRef();
}
}
// OFFSET: 0x005708F0
// FUNCTION: fn_02429
CSurfaceDX9::~CSurfaceDX9() {
if (this->m_surface) {
this->m_surface->Release();
this->m_surface = nullptr;
}
}
// OFFSET: 0x00570CB0
// FUNCTION: fn_02438
std::uint32_t CSurfaceDX9::Width() {
return this->m_width;
}
// OFFSET: 0x00570C60
// FUNCTION: fn_02436
std::uint32_t CSurfaceDX9::Height() {
return this->m_height;
}
// OFFSET: 0x00570A20
// FUNCTION: fn_02432
bool CSurfaceDX9::Resize(int width, int height) {
if (this->m_surface) {
this->m_surface->Release();
this->m_surface = nullptr;
}
this->m_width = AlignDimension(width);
this->m_height = AlignDimension(height);
return SUCCEEDED(this->m_context->device->CreateOffscreenPlainSurface(
this->m_width, this->m_height, this->m_context->backBufferDescription.Format, D3DPOOL_DEFAULT,
&this->m_surface, nullptr
)) &&
this->m_surface;
}
// OFFSET: 0x00570CC0
// FUNCTION: fn_02439
SurfaceLock CSurfaceDX9::Lock() {
SurfaceLock output{};
D3DSURFACE_DESC description;
D3DLOCKED_RECT locked;
if (
this->m_surface && SUCCEEDED(this->m_surface->GetDesc(&description)) &&
SUCCEEDED(this->m_surface->LockRect(&locked, nullptr, 0))
) {
output.success = true;
output.pixels = locked.pBits;
output.pitch = locked.Pitch;
output.width = description.Width;
output.height = description.Height;
output.bitsPerPixel = BitsPerPixel(description.Format);
}
return output;
}
// OFFSET: 0x00570DB0
// FUNCTION: fn_02442
void CSurfaceDX9::Unlock() {
if (this->m_surface) {
this->m_surface->UnlockRect();
}
}
// OFFSET: 0x00570C30
// FUNCTION: fn_02435
bool CSurfaceDX9::GetDC(HDC* output) {
return this->m_surface && SUCCEEDED(this->m_surface->GetDC(output));
}
// OFFSET: 0x00570D50
// FUNCTION: fn_02440
void CSurfaceDX9::ReleaseDC(HDC dc) {
if (this->m_surface) {
this->m_surface->ReleaseDC(dc);
}
}
// OFFSET: 0x00570DC0
// FUNCTION: fn_02443
void CSurfaceDX9::Upload(int x, int y, int width, int height, const std::uint32_t* pixels, int, int sourceWidth) {
if (!this->m_surface || !width || !height) {
return;
}
D3DSURFACE_DESC description;
if (FAILED(this->m_surface->GetDesc(&description))) {
return;
}
RECT rectangle{x, y, x + width, y + height};
D3DLOCKED_RECT locked;
if (FAILED(this->m_surface->LockRect(&locked, &rectangle, 0))) {
return;
}
const int stride = sourceWidth ? sourceWidth : width;
if (BitsPerPixel(description.Format) == 16) {
for (int row = 0; row < height; ++row) {
auto* output = reinterpret_cast<std::uint16_t*>(static_cast<unsigned char*>(locked.pBits) + row * locked.Pitch);
for (int column = 0; column < width; ++column) {
const std::uint32_t color = pixels[row * stride + column];
std::uint16_t value = 0;
if ((color & 0xFFFFFF) != 0xFF00FF) {
switch (description.Format) {
case D3DFMT_R5G6B5:
value = ((color & 255) >> 3) | ((color >> 5) & 0x7E0) | ((color >> 8) & 0xF800);
break;
case D3DFMT_X1R5G5B5:
case D3DFMT_A1R5G5B5:
value = Color1555(color);
break;
case D3DFMT_A4R4G4B4:
value = ((color >> 16) & 0xF000) | ((color & 255) >> 4) | ((color >> 8) & 0xF0) | ((color >> 12) & 0xF00);
break;
default:
break;
}
}
output[column] = value;
}
}
} else if (description.Format == D3DFMT_A8R8G8B8 || description.Format == D3DFMT_X8R8G8B8) {
for (int row = 0; row < height; ++row) {
auto* output = reinterpret_cast<std::uint32_t*>(static_cast<unsigned char*>(locked.pBits) + row * locked.Pitch);
for (int column = 0; column < width; ++column) {
const std::uint32_t color = pixels[row * stride + column];
output[column] = (color & 0xFFFFFF) == 0xFF00FF ? 0 : color;
}
}
}
this->m_surface->UnlockRect();
}
// OFFSET: 0x005709F0
// FUNCTION: fn_02431
void CSurfaceDX9::Fill(const RECT* rectangle, std::uint32_t color) {
if (this->m_surface) {
this->m_context->device->ColorFill(this->m_surface, rectangle, color);
}
}
// OFFSET: 0x00570AA0
// FUNCTION: fn_02433
void CSurfaceDX9::Draw(int x, int y, int width, int height, std::uint32_t) {
if (!this->m_surface) {
return;
}
D3DSURFACE_DESC description;
if (FAILED(this->m_surface->GetDesc(&description))) {
return;
}
const RECT target{x, y, x + width, y + height};
const RECT destination = Clip(target, this->m_context->backBufferDescription);
const RECT original{0, 0, destination.right - destination.left, destination.bottom - destination.top};
const RECT source = Clip(original, description);
this->m_context->device->StretchRect(
this->m_surface, &source, this->m_context->backBuffer, &destination, D3DTEXF_NONE
);
}
// OFFSET: 0x00570BA0
// FUNCTION: fn_02434
void CSurfaceDX9::Stretch(int x, int y, int width, int height) {
if (!this->m_surface) {
return;
}
const RECT target{x, y, x + width, y + height};
const RECT destination = Clip(target, this->m_context->backBufferDescription);
this->m_context->device->StretchRect(
this->m_surface, nullptr, this->m_context->backBuffer, &destination, D3DTEXF_LINEAR
);
}