2

我正在使用 D3DXSaveSurfaceToFile 将窗口化的 Direct3D 9 表面保存为 PNG、BMP 和 JPG 文件。D3DXSaveSurfaceToFile 调用没有返回任何错误,并且所有文件都可以在 Windows 照片查看器和画图中正常打开。但它们不会在 Paint Shop Pro 或 Photoshop 等更高端的图像编辑程序中打开。这些程序的错误消息基本上表明文件已损坏。如果我在 Paint 中打开文件,然后将它们保存为具有不同文件名的相同文件格式,那么它们将在其他程序中正常打开。

这让我相信 D3DXSaveSurfaceToFile 正在写出这些文件格式的非标准版本。有什么方法可以让这个函数写出可以在 Photoshop 等程序中打开的文件,而无需在 Paint 中重新保存文件的中间步骤?或者我应该使用另一个功能来更好地将 Direct3D 表面保存到图像?

4

3 回答 3

2

在图像元查看器中查看文件。它告诉你什么?

于 2010-03-18T21:50:06.693 回答
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();
    }
}
于 2010-03-18T21:57:46.467 回答
1

事实证明,这是我的代码中的错误和 Paint 在读取文件时比 Photoshop 更宽容的组合。我的代码中的错误导致文件以错误的扩展名保存(即 Image.bmp 实际上是使用 D3DXIFF_JPG 保存的)。当打开一个包含 JPG 图像但具有 BMP 扩展名的文件时,Photoshop 只是失败了该文件。我猜 Paint 起作用了,因为它忽略了文件扩展名,只是解码了文件内容。

图像元查看器中查看文件帮助我发现了问题。

于 2010-03-18T23:21:56.920 回答