DirectX surfaces stored in video memory can be rendered a lot faster than images rendered through GDI+ (object oriented graphics device interface windows api). GDI+, on the other hand, has support for saving images to a lot more formats.
I wanted to save a DirectX surface as a JPEG. There's a Direct3D extension function that supports this; D3DXSaveSurfaceToFile(). Unfortunately it only works with surfaces that are square (width = height). So to save a non-square JPEG I needed to convert from an IDirect3DSurface9 to a GDI+ Bitmap class then use GDI+'s encoders to save it as a JPEG.
To do this, immediately after the call to IDirect3DDevice->Present() do the following:
- Get the current render target via GetRenderTarget(0), then get the dimensions of the render target via GetDesc().
- Create a buffer in system memory (not video memory) to hold a copy of the current display. This is done with CreateOffscreenPlainSurface() w/ D3DPOOL_SYSTEMMEM
- use GetRenderTargetData() to copy (Blit) the current display to the offscreen buffer.
- Get a pointer to the offscreen pixel buffer via LockRect().
- Create a GDI+ Bitmap class using the constructor that takes width, height, pitch, pixel format and a pointer to the pixel buffer.
- Use the pitch of the offscreen pixel buffer (get it via GetDesc()) not the pitch of the original surface
- The pixel format of the bitmap must match the pixel format of the surface - this was specified when the IDirect3DDevice was created.
- Use the pitch of the offscreen pixel buffer (get it via GetDesc()) not the pitch of the original surface
- use the Bitmap->Save() (defined in Image which is inherited by Bitmap) method to write the file to a JPEG.
- unlock the rect via UnlockRect() and release any COM interface pointers that were either directly created via QueryInterface() or were indirectly created by calling an interface returning function.
The GDI+ API docs have an excellent description of using the encoders along with a very handy GetEncoderClsid() function.
No comments:
Post a Comment