views:

157

answers:

3

I'm using D3DXSaveSurfaceToFile to save windowed Direct3D 9 surfaces to PNG, BMP and JPG files. There are no errors returned from the D3DXSaveSurfaceToFile call and all files open fine in Windows Photo Viewer and Paint. But they will not open in a higher end image editing program such as Paint Shop Pro or Photoshop. The error messages from these programs basically say that the file is corrupted. If I open the files in Paint and then save them in the same file format with a different file name, then they'll open fine in the other programs.

This leads me to believe that D3DXSaveSurfaceToFile is writing out non-standard versions of these file formats. Is there some way I can get this function to write out files that can be opened in programs like Photoshop without the intermediate step of resaving the files in Paint? Or is there another function I should be using that does a better job of saving a Direct3D surfaces to an image?

+1  A: 

Take a look at the file in a image meta viewer. What does it tell you?

Byron Whitlock
Turns out the files were being saved incorrectly. Thanks for pointing me to that utility.
bsruth
If his solution worked... don't forget to accept it as the solution.
Lee
I upvoted, since it helped find the answer, but wasn't the exact answer for the question.
bsruth
+1  A: 

Unfortunately D3DXSaveSurfaceToFile() isn't the most stable (it's also exceptionally slow). Personally I do something like the below code. It works even on Anti-aliased displays by doing an offscreen render to take the screenshot then getting it into a buffer. It also supports only the most common of the pixel formats. Sorry for any errors in it, pulled it out of an app I used to work on.

You can then, in your code and probably in another thread, then convert said 'bitmap' to anything you like using a variety of different code.

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();
    }
}
Lee
A: 

Turns out that it was a combination of a bug in my code and Paint being more forgiving than Photoshop when it comes to reading files. The bug in my code caused the files to be saved with the wrong extension (i.e. Image.bmp was actually saved using D3DXIFF_JPG). When opening a file that contained a JPG image, but had a BMP extension, Photoshop just failed the file. I guess Paint worked since it ignored the file extension and just decoded the file contents.

Looking at a file in an image meta viewer helped me to see the problem.

bsruth