我正在使用D3DXSaveSurfaceToFile将窗口化的Direct3D 9曲面保存为PNG,BMP和JPG文件。 D3DXSaveSurfaceToFile调用没有返回任何错误,并且所有文件在Windows Photo Viewer和Paint中都可以正常打开。但它们不会在高端图像编辑程序中打开,例如Paint Shop Pro或Photoshop。来自这些程序的错误消息基本上说该文件已损坏。如果我在Paint中打开文件,然后使用不同的文件名以相同的文件格式保存它们,那么它们将在其他程序中打开。
这让我相信D3DXSaveSurfaceToFile正在写出这些文件格式的非标准版本。有没有什么方法可以让这个函数写出可以在像Photoshop这样的程序中打开的文件而无需在Paint中重新保存文件的中间步骤?或者我应该使用另一个功能,它可以更好地将Direct3D曲面保存到图像中吗?
答案 0 :(得分:2)
查看图片meta viewer中的文件。它告诉你什么?
答案 1 :(得分:2)
不幸的是D3DXSaveSurfaceToFile()不是最稳定的(它也非常慢)。就个人而言,我做了类似下面的代码。它甚至可以在消除锯齿的显示器上工作,通过执行屏幕外渲染来获取屏幕截图然后将其放入缓冲区。它还仅支持最常见的像素格式。对于其中的任何错误,请将其从我以前处理的应用中删除。
然后,您可以在您的代码中以及可能在另一个线程中,然后使用各种不同的代码将所述“位图”转换为您喜欢的任何内容。
void HandleScreenshot(IDirect3DDevice9* device)
{
DWORD tcHandleScreenshot = GetTickCount();
LPDIRECT3DSURFACE9 pd3dsBack = NULL;
LPDIRECT3DSURFACE9 pd3dsTemp = NULL;
// Grab the back buffer into a surface
if ( SUCCEEDED ( device->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &pd3dsBack) ))
{
D3DSURFACE_DESC desc;
pd3dsBack->GetDesc(&desc);
LPDIRECT3DSURFACE9 pd3dsCopy = NULL;
if (desc.MultiSampleType != D3DMULTISAMPLE_NONE)
{
if (SUCCEEDED(device->CreateRenderTarget(desc.Width, desc.Height, desc.Format, D3DMULTISAMPLE_NONE, 0, FALSE, &pd3dsCopy, NULL)))
{
if (SUCCEEDED(device->StretchRect(pd3dsBack, NULL, pd3dsCopy, NULL, D3DTEXF_NONE)))
{
pd3dsBack->Release();
pd3dsBack = pd3dsCopy;
}
else
{
pd3dsCopy->Release();
}
}
}
if (SUCCEEDED(device->CreateOffscreenPlainSurface(desc.Width, desc.Height, desc.Format, D3DPOOL_SYSTEMMEM, &pd3dsTemp, NULL)))
{
DWORD tmpTimeGRTD = GetTickCount();
if (SUCCEEDED(device->GetRenderTargetData(pd3dsBack, pd3dsTemp)))
{
D3DLOCKED_RECT lockedSrcRect;
if (SUCCEEDED(pd3dsTemp->LockRect(&lockedSrcRect, NULL, D3DLOCK_READONLY | D3DLOCK_NOSYSLOCK | D3DLOCK_NO_DIRTY_UPDATE)))
{
int nSize = desc.Width * desc.Height * 3;
BYTE* pixels = new BYTE[nSize +1];
int iSrcPitch = lockedSrcRect.Pitch;
BYTE* pSrcRow = (BYTE*)lockedSrcRect.pBits;
LPBYTE lpDest = pixels;
LPDWORD lpSrc;
switch (desc.Format)
{
case D3DFMT_A8R8G8B8:
case D3DFMT_X8R8G8B8:
for (int y = desc.Height - 1; y >= 0; y--)
{
lpSrc = reinterpret_cast<LPDWORD>(lockedSrcRect.pBits) + y * desc.Width;
for (unsigned int x = 0; x < desc.Width; x++)
{
*reinterpret_cast<LPDWORD>(lpDest) = *lpSrc;
lpSrc++; // increment source pointer by 1 DWORD
lpDest += 3; // increment destination pointer by 3 bytes
}
}
break;
default:
ZeroMemory(pixels, nSize);
}
pd3dsTemp->UnlockRect();
BITMAPINFOHEADER header;
header.biWidth = desc.Width;
header.biHeight = desc.Height;
header.biSizeImage = nSize;
header.biSize = sizeof(BITMAPINFOHEADER);
header.biPlanes = 1;
header.biBitCount = 3 * 8; // RGB
header.biCompression = 0;
header.biXPelsPerMeter = 0;
header.biYPelsPerMeter = 0;
header.biClrUsed = 0;
header.biClrImportant = 0;
BITMAPFILEHEADER bfh = {0};
bfh.bfType = 0x4d42;
bfh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
bfh.bfSize = bfh.bfOffBits + nSize;
unsigned int rough_size = sizeof(BITMAPINFOHEADER) + sizeof(BITMAPFILEHEADER) + nSize;
unsigned char* p = new unsigned char[rough_size]
memcpy(p, &bfh, sizeof(BITMAPFILEHEADER));
p += sizeof(BITMAPFILEHEADER);
memcpy(p, &header, sizeof(BITMAPINFOHEADER));
p += sizeof(BITMAPINFOHEADER);
memcpy(p, pixels, nSize);
delete [] pixels;
/**********************************************/
// p now has a full BMP file, write it out here
}
}
pd3dsTemp->Release();
}
pd3dsBack->Release();
}
}
答案 2 :(得分:1)
事实证明,这是我的代码中的一个错误和Paint在阅读文件时比Photoshop更宽容的组合。我的代码中的错误导致文件以错误的扩展名保存(即Image.bmp实际上是使用D3DXIFF_JPG保存的)。打开包含JPG图像但具有BMP扩展名的文件时,Photoshop刚刚失败了该文件。我猜Paint工作,因为它忽略了文件扩展名,只是解码了文件内容。
查看image meta viewer中的文件有助于我发现问题。