Jump to content
Search In
  • More options...
Find results that contain...
Find results in...

[DX8] Screenshot Map/Game


Recommended Posts

Hey guys, 

With the new DirectX8 engines, one of the functionalties that were lost from the DX7 engines was the inability to screenshot the map and/or the client screen. This meant that mappers couldn't showcase their awesome mapping talent and admins couldn't create screenshots of their games directly from the engine and has to resort to other tactics. Well, this tutorial solves that problem. You can now create as many screenshots as you possibly want. 

Attached to this post/topic is a module called "modAdvDX8Tex.bas", this module contains all the functions necssary to convert the game screen into a valid PNG, BMP or JPEG file. I've made sure to comment and document as much of the code as possible so that people can learn from it if they so desire. The actual module has been written for Eclipse Worlds, but it'll work with any and all DX8 engines. All that has to be changes is the usage of said module

**WARNING: THIS TUTORIAL ASSUMES THAT YOU HAVE APPLIED [THIS](http://www.eclipseorigins.com/index.php?/topic/136966-bug-gdip-bug-in-dx8-engines/) BUG FIX.**

**Steps to add in the module to your ****project:**

1. Put the module in the client src folder and open up your client project file.
2. Press Ctrl+D or go to Project -> Add file.

Here are a few source tutorials on how to actually use the module:

>! **Taking a screenshot of the game viewport:**

Find the method **Render_Graphics** in your rendering/graphics module

At the end of the method, ust after the call to the **Direct3D_Device.Present, **add this line of code: 

Call SaveDirectX8SurfToFile(App.Path & "\screenshot.png", Direct3D_Device.GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO), ImageFormats.PNG)

**Taking a screenshot of the entire map: **

–coming soon--

The provided module does not have any error handling functions. Error handling should be added in by you based upon the engine you are using. 


Link to comment
Share on other sites

The source code for the attached file is here, just in case it's lost during in the future due to forum conversions or some other reason. 

>! ```
Option Explicit

' This method allows you to convert your Direct3DTexture8 or Direct3DSurface8 texture into an image file

Public Enum ImageFormats ' All the image formats supported by GDIp
End Enum

Public Function ConvertSurfaceToArray(Surface As Direct3DSurface8, ByRef Pixels() As Byte) As Boolean
    Dim SurfDesc As D3DSURFACE_DESC, LockedRect As D3DLOCKED_RECT, rc As DxVBLib.RECT
    Dim TempSurf As Direct3DSurface8

    ' This methods converts a Direct3DSurface8 using a format ARGB with 8 bits
    'per component into an array of bytes

    ' You can get the ARGB value in the form of a long by copying 4 bytes (starting from the first) using CopyMemory
    ' i.e CopyMemory Long, byte(count), 4

    ' Each individual component of the ARGB can be extracted directly from the array
    ' For eg. starting from 0, x = 0 -> A = Arr(x + 3), R = Arr(x + 2), G = Arr(x + 1), B = Arr(x)

    ' Currently this method only provides support for 32bpp images. Though you can add support for 24bpp or 16bpp or 8bpp.
    ' Contact me if you want more help with that.

    Call Surface.GetDesc(SurfDesc)

    ' Check if it's a format that we can convert to a byte array
    If SurfDesc.Format = D3DFMT_A8R8G8B8 Or D3DFMT_X8R8G8B8 Then
        ' Set up the rect so that DX8 knows which parts of the surface we want to copy from
        With rc ' This will select the entire surface
            .Left = 0
            .Right = SurfDesc.Width
            .Top = 0
            .Bottom = SurfDesc.Height
        End With

        If SurfDesc.Pool = D3DPOOL_DEFAULT Then
            ' If the Texture is in the D3DPool_Default pool then we can't lock the rect
            ' We have make a copy of the Surface elsewhere before we attemptt lock it
            Set TempSurf = Direct3D_Device.CreateImageSurface(SurfDesc.Width, SurfDesc.Height, SurfDesc.Format)
            ' Copy the rect to the TempSurf
            Call Direct3D_Device.CopyRects(Surface, rc, 1, TempSurf, rc)
            ' If not in the Default pool then just set the TempSurf to the original Surf.
            Set TempSurf = Surface
        End If

        ' Lock the surface to get the pixel data.
        Call TempSurf.LockRect(LockedRect, ByVal 0, 0)
        ReDim Pixels((LockedRect.Pitch * SurfDesc.Height) - 1) ' Pitch = Bytes Per Pixel * width
        If Not DXCopyMemory(Pixels(0), ByVal LockedRect.pBits, LockedRect.Pitch * SurfDesc.Height) = D3D_OK Then
            ' We weren't able to copy the data
            ConvertSurfaceToArray = False
            Exit Function
        End If
        ' If you want, you can tweak the Pixels array. To put it back into the DX8 texture copy back into pbits.
        ' Just reverse the Dest and Source parameters in DXCopyMemory. Make sure UnLockRect is called. Else it the texture won't update

        Call TempSurf.UnlockRect ' This call is essential. If LockRect is called and this isn't called then it can lead to memory leaks

        ConvertSurfaceToArray = True
    End If
End Function

Public Function ConvertTextureToArray(Texture As Direct3DTexture8, Pixels() As Byte) As Boolean
    ConvertTextureToArray = ConvertSurfaceToArray(Texture.GetSurfaceLevel(0), Pixels)
End Function

Public Sub SaveDirectX8SurfToMemory(Data() As Byte, Surf As Direct3DSurface8, Optional ByVal ImageType As ImageFormats = PNG)
    Dim GDIToken As cGDIpToken, renderer As cGDIpRenderer, Image As cGDIpImage, I As Long
    Dim PixelFormat As ImageColorFormatConstants, Width As Long, Height As Long, SurfDesc As D3DSURFACE_DESC
    Dim ImageStride As Long, DataPointer As Long, Pixels() As Byte

    If Not ConvertSurfaceToArray(Surf, Pixels) Then Exit Sub

    Call Surf.GetDesc(SurfDesc)

    Set GDIToken = New cGDIpToken
    Set renderer = New cGDIpRenderer
    Set Image = New cGDIpImage

    Call renderer.AttachTokenClass(GDIToken)
    I = renderer.CreateHGraphicsFromHWND(frmMain.hWnd)    ' afaik the hWnd can be the hWnd of any form in the project

    If Image.LoadPicture_FromNothing(SurfDesc.Width, SurfDesc.Height, I, GDIToken) Then
        ' Set up the values for locking the image
        PixelFormat = PixelFormat32bppPARGB

        Width = Image.Width: Height = Image.Height

        ' lock the GDIp Image
        DataPointer = Image.LockImageBits(ImageLockModeWrite, PixelFormat, 0, 0, Width, Height, ImageStride)
        If DataPointer > 0 Then
            ' Copy the data that we had in DirectX8 Texture to the GDIp image
            Call CopyMemory(ByVal DataPointer, Pixels(0), ImageStride * Height)
            ' Unlock the Gdip image so that the image updates, pass back the values that we used in LockImageBits
            Call Image.UnLockImageBits(PixelFormat, Width, Height, ImageStride, DataPointer)
        End If

        If ImageType = ImageFormats.PNG Then
            Call Image.SaveAsPNG(Data)
        ElseIf ImageType = ImageFormats.BMP Then
            Call Image.SaveAsBMP(Data)
        ElseIf ImageType = ImageFormats.JPEG Then
            Call Image.SaveAsJPG(Data)
            Call Image.SaveAsPNG(Data) ' Just save as a PNG
        End If
    End If

    Set Image = Nothing
    Set renderer = Nothing
    Set GDIToken = Nothing

End Sub

Public Sub SaveDirectX8SurfToFile(ByVal Path As String, Surf As Direct3DSurface8, Optional ByVal ImageType As ImageFormats = PNG)
    Dim Data() As Byte, f As Long

    ' Convert the texture into a readable file
    Call SaveDirectX8SurfToMemory(Data, Surf, ImageType)

    ' Dump the data we got into the file at the path

    If FileExist(Path, True) Then Kill Path

    f = FreeFile
    Open Path For Binary As #f
    Put #f, , Data
    Close #f

    ' The file should now be a valid image
End Sub

Public Sub SaveDirectX8TextureToMemory(Data() As Byte, Texture As Direct3DTexture8, Optional ByVal ImageType As ImageFormats = ImageFormats.PNG)
    Call SaveDirectX8SurfToMemory(Data, Texture.GetSurfaceLevel(0), ImageType)
End Sub
>! Public Sub SaveDirectX8TextureToFile(ByVal Path As String, Texture As Direct3DTexture8, Optional ByVal ImageType As ImageFormats = ImageFormats.PNG)
    Call SaveDirectX8SurfToFile(Path, Texture.GetSurfaceLevel(0), ImageType)
End Sub
>! ```
Link to comment
Share on other sites

  • 3 weeks later...
You could do something like this: 

for i = 1 to 255
if not fileexists(app.path & "\screenshot" & i & ".png") then
Call SaveDirectX8SurfToFile(App.Path & "\screenshot" & i & ".png", Direct3D_Device.GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO), ImageFormats.PNG)
saved = true
end if

If you want the date to be a part of the file name try something like this: 

Call SaveDirectX8SurfToFile(App.Path & "\screenshot" & now & ".png", Direct3D_Device.GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO), mageFormats.PNG)

That will result in a filename like this: Screenshot 07-05-2015 20:32:17.png

I haven't tested out that code so I'm not sure that'll work exactly as it's intended right off the bat.
Link to comment
Share on other sites

  • 6 years later...

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Create New...