Okay, so here's what I've got so far. It all seems to work just fine. I'm working on finishing up more image writers. You really only need one, but it's nice to have options.
I added this abstract method to IVideoDriver.h and then redeclared it in each of the C*Driver*.h files.
Code: Select all
//! Returns an image created from the last rendered frame.
virtual IImage* createScreenShot() = 0;
Then implemented each of the methods in the appropriate files...
Code: Select all
//! Returns an image created from the last rendered frame.
IImage* CSoftwareDriver::createScreenShot()
{
return new CImage(BackBuffer->getColorFormat(), BackBuffer);
}
//! Returns an image created from the last rendered frame.
IImage* CSoftwareDriver2::createScreenShot()
{
return new CImage(BackBuffer->getColorFormat(), BackBuffer);
}
//! Returns an image created from the last rendered frame.
IImage* CD3D8Driver::createScreenShot()
{
HRESULT hr;
// query the screen dimensions of the current adapter
D3DDISPLAYMODE displayMode;
pID3DDevice->GetDisplayMode(&displayMode);
// create the image surface to store the front buffer image [always A8R8G8B8]
LPDIRECT3DSURFACE8 lpSurface;
if (FAILED(hr = pID3DDevice->CreateImageSurface(displayMode.Width, displayMode.Height, D3DFMT_A8R8G8B8, &lpSurface)))
return 0;
// read the front buffer into the image surface
if (FAILED(hr = pID3DDevice->GetFrontBuffer(lpSurface)))
{
lpSurface->Release();
return 0;
}
// get the size of the main window
RECT clientRect;
{
POINT clientPoint;
clientPoint.x = 0;
clientPoint.y = 0;
ClientToScreen( (HWND)getExposedVideoData().D3D8.HWnd, &clientPoint );
clientRect.left = clientPoint.x;
clientRect.top = clientPoint.y;
clientRect.right = clientRect.left + ScreenSize.Width;
clientRect.bottom = clientRect.top + ScreenSize.Height;
}
// lock the surface that is our window area
D3DLOCKED_RECT lockedRect;
if (FAILED(lpSurface->LockRect(&lockedRect, &clientRect, D3DLOCK_READONLY)))
{
lpSurface->Release();
return 0;
}
// this could throw, but we aren't going to worry about that case very much
IImage* newImage = new CImage(ECF_A8R8G8B8, ScreenSize);
// d3d pads the image, so we need to copy the correct number of bytes
u32* pPixels = (u32*)newImage->lock();
if (pPixels)
{
u8 * sP = (u8 *)lockedRect.pBits;
u32* dP = (u32*)pPixels;
s32 y;
for (y = 0; y < ScreenSize.Height; ++y)
{
memcpy(dP, sP, ScreenSize.Width * 4);
sP += lockedRect.Pitch;
dP += ScreenSize.Width;
}
newImage->unlock();
}
// we can unlock and release the surface
lpSurface->UnlockRect();
// release the image surface
lpSurface->Release();
// return status of save operation to caller
return newImage;
}
//! Returns an image created from the last rendered frame.
IImage* CD3D9Driver::createScreenShot()
{
HRESULT hr;
// query the screen dimensions of the current adapter
D3DDISPLAYMODE displayMode;
pID3DDevice->GetDisplayMode(0, &displayMode);
// create the image surface to store the front buffer image [always A8R8G8B8]
LPDIRECT3DSURFACE9 lpSurface;
if (FAILED(hr = pID3DDevice->CreateOffscreenPlainSurface(displayMode.Width, displayMode.Height, D3DFMT_A8R8G8B8, D3DPOOL_SCRATCH, &lpSurface, 0)))
return 0;
// read the front buffer into the image surface
if (FAILED(hr = pID3DDevice->GetFrontBufferData(0, lpSurface)))
{
lpSurface->Release();
return 0;
}
// get the size of the main window
RECT clientRect;
{
POINT clientPoint;
clientPoint.x = 0;
clientPoint.y = 0;
ClientToScreen( (HWND)getExposedVideoData().D3D9.HWnd, &clientPoint );
clientRect.left = clientPoint.x;
clientRect.top = clientPoint.y;
clientRect.right = clientRect.left + ScreenSize.Width;
clientRect.bottom = clientRect.top + ScreenSize.Height;
}
// lock the entire surface
D3DLOCKED_RECT lockedRect;
if (FAILED(lpSurface->LockRect(&lockedRect, &clientRect, D3DLOCK_READONLY)))
{
lpSurface->Release();
return 0;
}
// this could throw, but we aren't going to worry about that case very much
IImage* newImage = new CImage(ECF_A8R8G8B8, ScreenSize);
// d3d pads the image, so we need to copy the correct number of bytes
u32* pPixels = (u32*)newImage->lock();
if (pPixels)
{
u8 * sP = (u8 *)lockedRect.pBits;
u32* dP = (u32*)pPixels;
s32 y;
for (y = 0; y < ScreenSize.Height; ++y)
{
memcpy(dP, sP, ScreenSize.Width * 4);
sP += lockedRect.Pitch;
dP += ScreenSize.Width;
}
newImage->unlock();
}
// we can unlock and release the surface
lpSurface->UnlockRect();
// release the image surface
lpSurface->Release();
// return status of save operation to caller
return newImage;
}
//! Returns an image created from the last rendered frame.
//! Returns an image created from the last rendered frame.
IImage* COpenGLDriver::createScreenShot()
{
IImage* newImage = new CImage(ECF_A8R8G8B8, ScreenSize);
u32* pPixels = (u32*)newImage->lock();
if (!pPixels)
{
newImage->drop();
return 0;
}
glReadPixels(0, 0, ScreenSize.Width, ScreenSize.Height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, pPixels);
// opengl images are inverted, so we have to fix that here.
s32 y0 = 0, y1 = ScreenSize.Height - 1;
for (; y0 < y1; ++y0, --y1)
{
u32* topRow = &pPixels[y0 * ScreenSize.Width];
u32* botRow = &pPixels[y1 * ScreenSize.Width];
s32 x;
for(x = 0; x < ScreenSize.Width; ++x)
{
u32 pixel = topRow[x];
topRow[x] = botRow[x];
botRow[x] = pixel;
}
}
newImage->unlock();
if (glGetError() != 0)
{
newImage->drop();
return 0;
}
return newImage;
}
As mentioned in my previous post I created an interface for writing the images to disk. I've still got a bit of work to do here, but it works just fine. I've got bmp, jpeg and possibly tga working. I want to get psd, pcx and png also. I'll post that code when I finish it.
Travis