Screen shoot crush

You discovered a bug in the engine, and you are sure that it is not a problem of your code? Just post it in here. Please read the bug posting guidelines first.
Post Reply
Pr3t3nd3r
Posts: 186
Joined: Tue Feb 08, 2005 6:02 pm
Location: Romania
Contact:

Screen shoot crush

Post by Pr3t3nd3r »

In window mode.
I have my computer set on 1024/768 and i create a irrlicht window size 1024/768.

(window size >= screen resolution )

When i use createScreenShot() an illegal memory ... is thrown.

The problem is in:
// 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();
}

When y = screen resolution - clientPoint.x the error is generated.

I think the problem is that somehow the screen surface is createad at a size of screen resolution.height - bar height - ? (window size >= screen resolution )
Last edited by Pr3t3nd3r on Sat Sep 23, 2006 11:55 pm, edited 2 times in total.
Galactic Dream Best RTS game
http://www.rageofwar.net
Engage in epic galactic warfare, guide your people through the galaxy! in the real time strategy game made with Irrlicht
http://www.evolutionvault.com
Pr3t3nd3r
Posts: 186
Joined: Tue Feb 08, 2005 6:02 pm
Location: Romania
Contact:

Post by Pr3t3nd3r »

it seems that even with window smaller than the screen resolution is happening if clientRect.bottom > ScreenSize.Height

... the solution is somethink like:
//for (y = 0; y < ScreenSize.Height - clientRect.top ; ++y)
//bad


Edit:
That code is not working well (bad screenshot no more crush) with smaller windows than the resolution...

The correct code should be something like this:

s32 maxHeight = ScreenSize.Height;
if (clientRect.bottom > desktopHeight)
maxHeight -= clientRect.bottom - desktopHeight;

But i don't know how to find out the desktopHeight.
Galactic Dream Best RTS game
http://www.rageofwar.net
Engage in epic galactic warfare, guide your people through the galaxy! in the real time strategy game made with Irrlicht
http://www.evolutionvault.com
vitek
Bug Slayer
Posts: 3919
Joined: Mon Jan 16, 2006 10:52 am
Location: Corvallis, OR

Post by vitek »

I'll have a solution shortly.
vitek
Bug Slayer
Posts: 3919
Joined: Mon Jan 16, 2006 10:52 am
Location: Corvallis, OR

Post by vitek »

Two things.
  1. It is called a crash not a crush.
  2. There will be an incompatibility between all of the other drivers and the D3D drivers. The D3D drivers can only capture the part of the image that is on screen. The other drivers capture the whole image.
As a solution, I could fix the code to return NULL if the entire window is not on the screen. I could fix it just for the D3D drivers, or for all of them. The other option would be to capture as much data as possible. For D3D you may just see the small part of the image that is on screen, but at least it would work.
vitek
Bug Slayer
Posts: 3919
Joined: Mon Jan 16, 2006 10:52 am
Location: Corvallis, OR

Post by vitek »

This code works for the D3D9 driver. The D3D8 driver is similar, just replace all occurances of 9 with 8 and change the GetFrontBufferData call to GetFrontBuffer and lose the first parameter.

As mentioned above the D3D drivers will give different results if the window is partially off screen. The screenshot that is captured is exactly what you see on the screen in the area occupied by the application window. If the task manager is sitting on top then you will see the task manager in the screenshot. This is not true for the OGL and software drivers.

Code: Select all

//! 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;
   }

   // find the size of the surface
   D3DSURFACE_DESC surfaceDesc;
   if (FAILED(lpSurface->GetDesc(&surfaceDesc)))
   {
      lpSurface->Release();
      return 0;
   }

   RECT clientRect;
   clientRect.top    = 0;
   clientRect.left   = 0;
   clientRect.bottom = clientRect.top  + surfaceDesc.Height;
   clientRect.right  = clientRect.left + surfaceDesc.Width;

   //if (!Fullscreen)
   {
      HWND hDriverWnd = (HWND)getExposedVideoData().D3D9.HWnd;

      RECT frameRect;
      GetClientRect(hDriverWnd, &frameRect);

      POINT topLeftCorner;
      topLeftCorner.x = 0;
      topLeftCorner.y = 0;
      ClientToScreen(hDriverWnd, &topLeftCorner);

      // move the frame rect into screen coordinates
      OffsetRect(&frameRect, topLeftCorner.x, topLeftCorner.y);

      // clip the client rect to the window size
      IntersectRect(&clientRect, &clientRect, &frameRect);
   }

   // lock the entire surface
   D3DLOCKED_RECT lockedRect;
   if (FAILED(lpSurface->LockRect(&lockedRect, &clientRect, D3DLOCK_READONLY)))
   {
      lpSurface->Release();
      return 0;
   }

   // this is the size of the image that we will generate
   core::dimension2d<s32> newImageSize(clientRect.right - clientRect.left,
                                       clientRect.bottom - clientRect.top);

   // this could throw, but we aren't going to worry about that case very much
   IImage* newImage = new CImage(ECF_A8R8G8B8, newImageSize);

   // 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 < newImageSize.Height; ++y)
      {
         memcpy(dP, sP, newImageSize.Width * 4);

         sP += lockedRect.Pitch;
         dP += newImageSize.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;
}
Post Reply