使用 D3DXSaveSurfaceToFile 保存的图像将在 Paint 中打开,而不是 Photoshop
题
我使用 D3DXSaveSurfaceToFile 将窗口化 Direct3D 9 曲面保存为 PNG、BMP 和 JPG 文件。D3DXSaveSurfaceToFile 调用没有返回任何错误,并且所有文件都可以在 Windows 照片查看器和画图器中正常打开。但它们无法在 Paint Shop Pro 或 Photoshop 等高端图像编辑程序中打开。这些程序的错误消息基本上表明文件已损坏。如果我在“画图”中打开文件,然后将它们保存为具有不同文件名的相同文件格式,那么它们将在其他程序中正常打开。
这让我相信 D3DXSaveSurfaceToFile 正在写出这些文件格式的非标准版本。有什么方法可以让这个函数写出可以在 Photoshop 等程序中打开的文件,而无需在 Paint 中重新保存文件的中间步骤吗?或者我应该使用另一个函数来更好地将 Direct3D 曲面保存到图像中?
解决方案 3
事实证明,这是我的代码中的错误和 Paint 在读取文件时比 Photoshop 更宽容的结合。我的代码中的错误导致文件以错误的扩展名保存(即Image.bmp实际上是使用D3DXIFF_JPG保存的)。当打开包含 JPG 图像但具有 BMP 扩展名的文件时,Photoshop 只是失败了该文件。我猜画图可以工作,因为它忽略了文件扩展名,只是解码了文件内容。
查看 a 中的文件 图像元查看器 帮助我看到了问题。
其他提示
看一看一个图像中元观察者中的文件。这是什么告诉你吗?
不幸的是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();
}
}