GrpImage/GrpSubImage verbatim; GrpImageTexture DDS path through CDXTCImage header + low-texture-memory mip bias, CreateDeviceObjects/CreateFromDiskFile verbatim; GrpTexture::SetTextureStage via STATEMANAGER. Port-map entries for GrpTexture/Image/SubImage/ImageTexture/IndexBuffer/VertexBuffer. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
159 lines
6.2 KiB
C++
159 lines
6.2 KiB
C++
// 40250 EterLib texture/image/buffer resources on the platform: CGraphicImageTexture (the DDS header check
|
|
// and its low-texture-memory mip bias, memory textures), CGraphicImage, CGraphicVertexBuffer/IndexBuffer.
|
|
#include "EterLib/StdAfx.h"
|
|
#include "EterLib/GrpImage.h"
|
|
#include "EterLib/GrpImageTexture.h"
|
|
#include "EterLib/GrpIndexBuffer.h"
|
|
#include "EterLib/GrpVertexBuffer.h"
|
|
#include "../../src/platform/EterLib/CpuBuffer.h"
|
|
#include "../../src/platform/EterLib/RecordingDevice.h"
|
|
#include "../../src/platform/EterLib/UIRenderCommands.h"
|
|
|
|
#include <cstdio>
|
|
#include <cstring>
|
|
#include <vector>
|
|
|
|
namespace {
|
|
int g_failures = 0;
|
|
void check(bool ok, const char* name)
|
|
{
|
|
if (!ok) {
|
|
std::fprintf(stderr, "FAIL %s\n", name);
|
|
++g_failures;
|
|
}
|
|
}
|
|
|
|
struct BaseAccess : CGraphicBase
|
|
{
|
|
static void SetDevice(IDirect3DDevice8* device) { ms_lpd3dDevice = device; }
|
|
static void SetLowTextureMemory(bool low) { ms_isLowTextureMemory = low; }
|
|
};
|
|
|
|
// "DDS " + DDSURFACEDESC2 (LINEARSIZE, MIPMAPCOUNT) + the DXT1 levels of a width x height texture.
|
|
std::vector<BYTE> MakeDDS(DWORD width, DWORD height, DWORD mips)
|
|
{
|
|
std::vector<BYTE> dds(128, 0);
|
|
memcpy(&dds[0], "DDS ", 4);
|
|
DWORD* h = reinterpret_cast<DWORD*>(&dds[4]);
|
|
h[0] = 124;
|
|
h[1] = 0x1 | 0x2 | 0x4 | 0x1000 | 0x20000 | 0x80000;
|
|
h[2] = height;
|
|
h[3] = width;
|
|
h[4] = width * height / 2;
|
|
h[6] = mips;
|
|
h[18] = 32;
|
|
h[19] = 0x4;
|
|
memcpy(&h[20], "DXT1", 4);
|
|
for (DWORD size = width * height / 2, i = 0; i < mips; ++i, size >>= 2)
|
|
dds.resize(dds.size() + std::max<DWORD>(size, 8), 0);
|
|
return dds;
|
|
}
|
|
|
|
struct ImageAccess : CGraphicImage
|
|
{
|
|
ImageAccess(const char* name) : CGraphicImage(name) {}
|
|
bool Load(int size, const void* data) { return OnLoad(size, data); }
|
|
};
|
|
|
|
std::vector<BYTE> MakePNGHeader(DWORD width, DWORD height)
|
|
{
|
|
std::vector<BYTE> png = {0x89, 'P', 'N', 'G', '\r', '\n', 0x1a, '\n', 0, 0, 0, 13, 'I', 'H', 'D', 'R'};
|
|
for (DWORD v : {width, height})
|
|
for (int shift = 24; shift >= 0; shift -= 8)
|
|
png.push_back(BYTE(v >> shift));
|
|
png.resize(160, 0); // past the 128 bytes the DDS header check reads
|
|
return png;
|
|
}
|
|
}
|
|
|
|
int main()
|
|
{
|
|
IDirect3DDevice8* device = MtCreateRecordingDevice(800, 600);
|
|
BaseAccess::SetDevice(device);
|
|
BaseAccess::SetLowTextureMemory(false);
|
|
|
|
// A DXT .dds goes through CDXTCImage::LoadHeaderFromMemory + CreateDDSTexture; the handle names the file.
|
|
{
|
|
std::vector<BYTE> dds = MakeDDS(64, 32, 3);
|
|
CGraphicImageTexture texture;
|
|
texture.SetFileName("d:\\ymir work\\ui\\a.dds");
|
|
check(texture.CreateFromMemoryFile(UINT(dds.size()), dds.data(), D3DFMT_UNKNOWN, 0), "DDS create");
|
|
check(!texture.IsEmpty() && texture.GetWidth() == 64 && texture.GetHeight() == 32, "DDS size");
|
|
check(UIRenderTextureNameFromHandle(texture.GetD3DTexture()) == "d:\\ymir work\\ui\\a.dds", "DDS handle name");
|
|
}
|
|
|
|
// Low texture memory skips the top mip of a mipmapped .dds (40250 halves the reported size) ...
|
|
BaseAccess::SetLowTextureMemory(true);
|
|
{
|
|
std::vector<BYTE> dds = MakeDDS(64, 32, 3);
|
|
CGraphicImageTexture texture;
|
|
texture.SetFileName("b.dds");
|
|
check(texture.CreateFromMemoryFile(UINT(dds.size()), dds.data(), D3DFMT_UNKNOWN, 0), "low-memory DDS create");
|
|
check(texture.GetWidth() == 32 && texture.GetHeight() == 16, "low-memory DDS half size");
|
|
}
|
|
// ... but not one with a single level.
|
|
{
|
|
std::vector<BYTE> dds = MakeDDS(64, 32, 1);
|
|
CGraphicImageTexture texture;
|
|
texture.SetFileName("c.dds");
|
|
check(texture.CreateFromMemoryFile(UINT(dds.size()), dds.data(), D3DFMT_UNKNOWN, 0) && texture.GetWidth() == 64,
|
|
"low-memory single-level DDS keeps its size");
|
|
}
|
|
BaseAccess::SetLowTextureMemory(false);
|
|
|
|
// Other formats (D3DXCreateTextureFromFileInMemoryEx): the size comes from the file header.
|
|
{
|
|
std::vector<BYTE> png = MakePNGHeader(300, 200);
|
|
CGraphicImageTexture texture;
|
|
texture.SetFileName("d.png");
|
|
check(texture.CreateFromMemoryFile(UINT(png.size()), png.data(), D3DFMT_UNKNOWN, 0), "PNG create");
|
|
check(texture.GetWidth() == 300 && texture.GetHeight() == 200, "PNG size");
|
|
check(!texture.CreateFromMemoryFile(4, "junk", D3DFMT_UNKNOWN, 0), "unknown bytes refused");
|
|
}
|
|
|
|
// CGraphicImage::OnLoad (CResource::Load with the pack bytes) sets the rect from the texture; Clear empties it.
|
|
{
|
|
std::vector<BYTE> png = MakePNGHeader(40, 24);
|
|
ImageAccess image("e.png");
|
|
check(image.Load(int(png.size()), png.data()), "CGraphicImage OnLoad");
|
|
check(image.GetWidth() == 40 && image.GetHeight() == 24 && image.GetRectReference().right == 40, "CGraphicImage rect");
|
|
image.Clear();
|
|
check(image.IsEmpty() && image.GetRectReference().right == 0, "CGraphicImage Clear");
|
|
}
|
|
|
|
// A memory texture (font page): Lock hands out A4R4G4B4 words, Unlock republishes them as ARGB32.
|
|
{
|
|
CGraphicImageTexture texture;
|
|
check(texture.Create(4, 2, D3DFMT_A4R4G4B4, 0) && !texture.IsEmpty(), "memory texture create");
|
|
int pitch = 0;
|
|
void* pixels = NULL;
|
|
check(texture.Lock(&pitch, &pixels) && pitch == 8, "memory texture lock");
|
|
static_cast<WORD*>(pixels)[5] = 0xF84C;
|
|
texture.Unlock();
|
|
UIMemoryTexture out;
|
|
const std::string name = UIRenderTextureName(&texture);
|
|
check(UIRenderMemoryTexture(name, &out) && out.width == 4 && out.argb[5] == 0xFF8844CC, "memory texture pixels");
|
|
check(name.find("@1") != std::string::npos, "Unlock bumps the revision");
|
|
}
|
|
|
|
// Vertex and index buffers keep their bytes in memory; Unlock bumps the revision the renderer checks.
|
|
{
|
|
CGraphicVertexBuffer vb;
|
|
check(vb.Create(3, D3DFVF_XYZ | D3DFVF_DIFFUSE, 0, D3DPOOL_MANAGED) && vb.GetVertexStride() == 16 && vb.GetVertexCount() == 3,
|
|
"vertex buffer create");
|
|
const float data[12] = {1, 2, 3, 0, 4, 5, 6, 0, 7, 8, 9, 0};
|
|
check(vb.Copy(sizeof(data), data), "vertex buffer copy");
|
|
auto* cpu = static_cast<MtCpuVertexBuffer*>(vb.GetD3DVertexBuffer());
|
|
check(memcmp(cpu->bytes.data(), data, sizeof(data)) == 0 && cpu->revision == 1, "vertex bytes");
|
|
|
|
CGraphicIndexBuffer ib;
|
|
TFace faces[2] = {{{0, 1, 2}}, {{2, 1, 3}}};
|
|
check(ib.Create(2, faces) && ib.GetIndexCount() == 6, "index buffer from faces");
|
|
auto* idx = static_cast<MtCpuIndexBuffer*>(ib.GetD3DIndexBuffer());
|
|
const WORD* w = reinterpret_cast<const WORD*>(idx->bytes.data());
|
|
check(w[3] == 2 && w[5] == 3 && idx->format == D3DFMT_INDEX16, "index words");
|
|
}
|
|
|
|
return g_failures ? 1 : 0;
|
|
}
|