mirror of
https://git.huckle.dev/Huckles-Minecraft-Archive/LCE-Revelations.git
synced 2026-06-09 10:52:32 +00:00
fix: capture save thumbnails directly from D3D11 backbuffer
The new 4JLibs CaptureThumbnail produces all-black 64x64 PNGs because its internal m_backBufferTexture copy fails silently. Bypass it entirely and capture from the swap chain backbuffer using the same proven approach as our F2 screenshot, with center-crop and downsample to 64x64 PNG.
This commit is contained in:
@@ -9,6 +9,11 @@
|
||||
#include "../../Minecraft.World/LevelSettings.h"
|
||||
#include "../../Minecraft.World/BiomeSource.h"
|
||||
#include "../../Minecraft.World/LevelType.h"
|
||||
#include "stb_image_write.h"
|
||||
|
||||
extern ID3D11Device* g_pd3dDevice;
|
||||
extern ID3D11DeviceContext* g_pImmediateContext;
|
||||
extern IDXGISwapChain* g_pSwapChain;
|
||||
|
||||
CConsoleMinecraftApp app;
|
||||
|
||||
@@ -33,9 +38,120 @@ void CConsoleMinecraftApp::FatalLoadError()
|
||||
{
|
||||
}
|
||||
|
||||
static const int THUMBNAIL_SIZE = 64;
|
||||
|
||||
void CConsoleMinecraftApp::CaptureSaveThumbnail()
|
||||
{
|
||||
RenderManager.CaptureThumbnail(&m_ThumbnailBuffer);
|
||||
if (!g_pSwapChain || !g_pd3dDevice || !g_pImmediateContext)
|
||||
return;
|
||||
|
||||
// Release any previous capture
|
||||
if (m_ThumbnailBuffer.Allocated())
|
||||
m_ThumbnailBuffer.Release();
|
||||
|
||||
// Get the backbuffer
|
||||
ID3D11Texture2D* pBackBuffer = nullptr;
|
||||
HRESULT hr = g_pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&pBackBuffer);
|
||||
if (FAILED(hr))
|
||||
return;
|
||||
|
||||
D3D11_TEXTURE2D_DESC backDesc = {};
|
||||
pBackBuffer->GetDesc(&backDesc);
|
||||
|
||||
// Create a staging texture at backbuffer size to read pixels
|
||||
D3D11_TEXTURE2D_DESC stagingDesc = backDesc;
|
||||
stagingDesc.Usage = D3D11_USAGE_STAGING;
|
||||
stagingDesc.BindFlags = 0;
|
||||
stagingDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
|
||||
stagingDesc.MiscFlags = 0;
|
||||
|
||||
ID3D11Texture2D* pStaging = nullptr;
|
||||
hr = g_pd3dDevice->CreateTexture2D(&stagingDesc, nullptr, &pStaging);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
pBackBuffer->Release();
|
||||
return;
|
||||
}
|
||||
|
||||
g_pImmediateContext->CopyResource(pStaging, pBackBuffer);
|
||||
pBackBuffer->Release();
|
||||
|
||||
D3D11_MAPPED_SUBRESOURCE mapped = {};
|
||||
hr = g_pImmediateContext->Map(pStaging, 0, D3D11_MAP_READ, 0, &mapped);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
pStaging->Release();
|
||||
return;
|
||||
}
|
||||
|
||||
// Downsample to THUMBNAIL_SIZE x THUMBNAIL_SIZE with simple box filter
|
||||
unsigned char* thumb = new unsigned char[THUMBNAIL_SIZE * THUMBNAIL_SIZE * 4];
|
||||
|
||||
// Crop to square (center crop), then scale down
|
||||
UINT srcSize = (backDesc.Width < backDesc.Height) ? backDesc.Width : backDesc.Height;
|
||||
UINT offsetX = (backDesc.Width - srcSize) / 2;
|
||||
UINT offsetY = (backDesc.Height - srcSize) / 2;
|
||||
|
||||
for (int ty = 0; ty < THUMBNAIL_SIZE; ty++)
|
||||
{
|
||||
for (int tx = 0; tx < THUMBNAIL_SIZE; tx++)
|
||||
{
|
||||
// Map thumbnail pixel to source region
|
||||
UINT sx = offsetX + (tx * srcSize) / THUMBNAIL_SIZE;
|
||||
UINT sy = offsetY + (ty * srcSize) / THUMBNAIL_SIZE;
|
||||
|
||||
const unsigned char* src = (const unsigned char*)mapped.pData + sy * mapped.RowPitch + sx * 4;
|
||||
unsigned char* dst = thumb + (ty * THUMBNAIL_SIZE + tx) * 4;
|
||||
|
||||
dst[0] = src[0]; // R (or B depending on format, but BGRA->RGBA swap below)
|
||||
dst[1] = src[1]; // G
|
||||
dst[2] = src[2]; // B
|
||||
dst[3] = 0xFF; // A
|
||||
}
|
||||
}
|
||||
|
||||
g_pImmediateContext->Unmap(pStaging, 0);
|
||||
pStaging->Release();
|
||||
|
||||
// If backbuffer is BGRA, swap to RGBA for PNG
|
||||
if (backDesc.Format == DXGI_FORMAT_B8G8R8A8_UNORM || backDesc.Format == DXGI_FORMAT_B8G8R8A8_UNORM_SRGB)
|
||||
{
|
||||
for (int i = 0; i < THUMBNAIL_SIZE * THUMBNAIL_SIZE; i++)
|
||||
{
|
||||
unsigned char tmp = thumb[i * 4];
|
||||
thumb[i * 4] = thumb[i * 4 + 2];
|
||||
thumb[i * 4 + 2] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
// Encode to PNG in memory using stbi_write_png_to_func
|
||||
struct PngBuffer { unsigned char* data; int size; int capacity; } pngBuf = {};
|
||||
pngBuf.capacity = THUMBNAIL_SIZE * THUMBNAIL_SIZE * 4 + 256;
|
||||
pngBuf.data = (unsigned char*)malloc(pngBuf.capacity);
|
||||
pngBuf.size = 0;
|
||||
|
||||
stbi_write_png_to_func([](void* ctx, void* data, int size) {
|
||||
PngBuffer* buf = (PngBuffer*)ctx;
|
||||
if (buf->size + size > buf->capacity)
|
||||
{
|
||||
buf->capacity = (buf->size + size) * 2;
|
||||
buf->data = (unsigned char*)realloc(buf->data, buf->capacity);
|
||||
}
|
||||
memcpy(buf->data + buf->size, data, size);
|
||||
buf->size += size;
|
||||
}, &pngBuf, THUMBNAIL_SIZE, THUMBNAIL_SIZE, 4, thumb, THUMBNAIL_SIZE * 4);
|
||||
delete[] thumb;
|
||||
|
||||
if (pngBuf.size > 0)
|
||||
{
|
||||
m_ThumbnailBuffer.m_type = ImageFileBuffer::e_typePNG;
|
||||
m_ThumbnailBuffer.m_pBuffer = pngBuf.data;
|
||||
m_ThumbnailBuffer.m_bufferSize = pngBuf.size;
|
||||
}
|
||||
else
|
||||
{
|
||||
free(pngBuf.data);
|
||||
}
|
||||
}
|
||||
void CConsoleMinecraftApp::GetSaveThumbnail(PBYTE *pbData,DWORD *pdwSize)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user