Capturar Foto de Juego !!

Iniciado por TrashAmbishion, 21 Junio 2016, 09:09 AM

0 Miembros y 3 Visitantes están viendo este tema.

Eleкtro

Traigo buenas noticias, he probado a hacer la captura con SharpDX y funciona de maravilla :). He usado el mismo ejemplo que el de GitHub, si necesitas ayuda con eso lo traduciré a VB.NET y te mostraré una función de uso genérico (hoy o mañana).

Saludos!








TrashAmbishion

Hola,

Muchas gracias espero entonces con el codigo que me dejastes anteriormente me tira la foto cuando esta en FullScreen en Negro completamente lo que no mire es en que resolucion estaba trabajando.

Y la otra me daba error me decia que no encontraba esa funcion o algo similar.

Bueno nada ya espero entonces por esta que trabaja a la par con DirectX.

Salu2 y gracias

PD: De todas formas aun quisiera sino te molesta tratar de ejecutar con exito el tuyo para tenerlo como otra opción. Despues vemos eso con mas calma para decirte el error exacto que me genera con la 2da funcion.

Eleкtro

#22
Cita de: TrashAmbishion en  2 Julio 2016, 22:33 PMMuchas gracias espero entonces con el codigo que me dejastes anteriormente me tira la foto cuando esta en FullScreen en Negro completamente lo que no mire es en que resolucion estaba trabajando.

Se toma en negro por que como intenté explicarte antes de tomar la captura primero debes minimizar y maximizar el juego para que la imagen estática del juego se actualice. Es complicado esta mierd@ de DirectX y no comprendo del todo como funciona en ese sentido, pero hay que hacer eso ...si utilizamos la metodología Win32/GDI.




Cita de: TrashAmbishion en  2 Julio 2016, 22:33 PMDe todas formas aun quisiera sino te molesta tratar de ejecutar con exito el tuyo para tenerlo como otra opción.

Lamentablemente eso no va a ser posible, no he logrdo solucionar el problema de no poder "actualizar" la imagen de forma automatizada (quiero decir, sin minimizar y maximizar manualmente la ventana del juego), y he probado técnicas que en un principio deberian funcionar (funciones: ShowWindow, SetForegroundwindow, etc) pero no, eso no funciona, DirectX parece trabajar de una forma compleja la ventana.

Si ni siquiera Windows por si solo ni tampoco (algunas)aplicaciones profesionales pueden tomar una captura en condiciones del juego cuando éste está en fullscreen, mucho menos soy capaz de hacerlo yo sin ser un experto en la materia de gráficos. Es complicado al menos.

Es necesario evitar las limitaciones gráficas del modelo Win32 y recurrir a una metodología más potente como DirectX para tomar ese tipo de captura, por ejemplo usando el wrapper de SharpDX para .NET.




Cita de: TrashAmbishion en  2 Julio 2016, 22:33 PMBueno nada ya espero entonces por esta que trabaja a la par con DirectX.

Aquí tienes. Con esta metodología basada en DirectX ya si que no va a haber problemas para tomar la captura del juego en fullscreen.

Código (vbnet) [Seleccionar]
Imports System.Drawing.Imaging
Imports System.IO

Imports SharpDX
Imports SharpDX.Direct3D11
Imports SharpDX.DXGI
Imports Device = SharpDX.Direct3D11.Device
Imports MapFlags = SharpDX.Direct3D11.MapFlags

'Imports Elektro.Imaging.Tools

Namespace Test

   Public NotInheritable Class SharpDXUtil

       ''' ----------------------------------------------------------------------------------------------------
       ''' <summary>
       ''' Capture the screen output using the default graphics card adapter.
       ''' <para></para>
       ''' This DirectX based methodology is useful to take screenshot of games that are running in full screen.
       ''' <para></para>
       ''' However, using this methodology for other common desktop screen captures will produce unexpected results (such as wrong colors);
       ''' so for common screenshots you should use the methods exposed in <see cref="Elektro.Imaging.Tools.ImageUtil"/> instead.
       ''' </summary>
       ''' ----------------------------------------------------------------------------------------------------
       ''' <returns>
       ''' The resulting <see cref="System.Drawing.Image"/>.
       ''' </returns>
       ''' ----------------------------------------------------------------------------------------------------
       Public Shared Function TakeScreenshot() As Image

           Return SharpDXUtil.TakeScreenshot(adapterIndex:=0)

       End Function

       ''' ----------------------------------------------------------------------------------------------------
       ''' <summary>
       ''' Captures the screen output using the specified graphics card adapter.
       ''' <para></para>
       ''' This DirectX based methodology is useful to take screenshot of games that are running in full screen.
       ''' <para></para>
       ''' However, using this methodology for other common desktop screen captures will produce unexpected results (such as wrong colors);
       ''' so for common screenshots you should use the methods exposed in <see cref="Elektro.Imaging.Tools.ImageUtil"/> instead.
       ''' </summary>
       ''' ----------------------------------------------------------------------------------------------------
       ''' <param name="adapterIndex">
       ''' The index of the graphics card adapter.
       ''' <para></para>
       ''' Set this value to <c>0</c> if you don't have more than one graphics card.
       ''' </param>
       ''' ----------------------------------------------------------------------------------------------------
       ''' <returns>
       ''' The resulting <see cref="System.Drawing.Image"/>.
       ''' </returns>
       ''' ----------------------------------------------------------------------------------------------------
       Public Shared Function TakeScreenshot(ByVal adapterIndex As Integer) As Image

           Dim factory As Factory1
           Dim adapter As Adapter1
           Dim device As Device
           Dim output As Output
           Dim output1 As Output1
           Dim scrSize As Size
           Dim textureDesc As Texture2DDescription
           Dim screenTexture As Texture2D
           Dim duplicatedOutput As OutputDuplication
           Dim captureDone As Boolean = False
           Dim capture As Bitmap = Nothing
           Dim i As Integer = 0

           factory = New Factory1()
           adapter = factory.GetAdapter1(adapterIndex)
           device = New Device(adapter)
           output = adapter.GetOutput(adapterIndex)
           output1 = output.QueryInterface(Of Output1)()
           scrSize = New Size(width:=CType(output.Description.DesktopBounds, SharpDX.Rectangle).Width,
                              height:=CType(output.Description.DesktopBounds, SharpDX.Rectangle).Height)

           ' Create Staging texture CPU-accessible.
           textureDesc = New Texture2DDescription() With {
               .CpuAccessFlags = CpuAccessFlags.Read,
               .BindFlags = BindFlags.None,
               .Format = Format.B8G8R8A8_UNorm,
               .Width = scrSize.Width,
               .Height = scrSize.Height,
               .OptionFlags = ResourceOptionFlags.None,
               .MipLevels = 1,
               .ArraySize = 1,
               .SampleDescription = New SampleDescription With {.Count = 1, .Quality = 0},
               .Usage = ResourceUsage.Staging
           }
           screenTexture = New Texture2D(device, textureDesc)
           duplicatedOutput = output1.DuplicateOutput(device)

           While Not (captureDone)
               Try
                   Dim screenResource As DXGI.Resource = Nothing

                   Dim duplicateFrameInformation As OutputDuplicateFrameInformation

                   ' Try to get duplicated frame within given time.
                   duplicatedOutput.AcquireNextFrame(10000, duplicateFrameInformation, screenResource)

                   If (i > 0) Then
                       ' Copy resource into memory that can be accessed by the CPU.
                       Using screenTexture2D As Texture2D = screenResource.QueryInterface(Of Texture2D)()
                           device.ImmediateContext.CopyResource(screenTexture2D, screenTexture)
                       End Using

                       ' Get the desktop capture texture.
                       Dim mapSource As DataBox = device.ImmediateContext.MapSubresource(screenTexture, 0, MapMode.Read, MapFlags.None)

                       ' Create the bitmap.
                       capture = New System.Drawing.Bitmap(scrSize.Width, scrSize.Height, PixelFormat.Format32bppArgb)
                       Dim boundsRect As New System.Drawing.Rectangle(0, 0, scrSize.Width, scrSize.Height)

                       ' Copy pixels from screen capture Texture to GDI bitmap.
                       Dim mapDest As BitmapData = capture.LockBits(boundsRect, ImageLockMode.WriteOnly, capture.PixelFormat)
                       Dim sourcePtr As IntPtr = mapSource.DataPointer
                       Dim destPtr As IntPtr = mapDest.Scan0

                       For y As Integer = 0 To (scrSize.Height - 1)
                           ' Copy a single line.
                           Utilities.CopyMemory(destPtr, sourcePtr, (scrSize.Width * 4))

                           ' Advance pointers.
                           sourcePtr = IntPtr.Add(sourcePtr, mapSource.RowPitch)
                           destPtr = IntPtr.Add(destPtr, mapDest.Stride)
                       Next y

                       capture.UnlockBits(mapDest)
                       device.ImmediateContext.UnmapSubresource(screenTexture, 0)
                       captureDone = True
                   End If

                   screenResource.Dispose()
                   duplicatedOutput.ReleaseFrame()

               Catch e As SharpDXException
                   If (e.ResultCode.Code <> DXGI.ResultCode.WaitTimeout.Result.Code) Then
                       Throw e
                   End If

               End Try

               i += 1

           End While

           If (duplicatedOutput IsNot Nothing) Then
               duplicatedOutput.Dispose()
           End If

           If (screenTexture IsNot Nothing) Then
               screenTexture.Dispose()
           End If

           If (output1 IsNot Nothing) Then
               output1.Dispose()
           End If

           If (output IsNot Nothing) Then
               output.Dispose()
           End If

           If (device IsNot Nothing) Then
               device.Dispose()
           End If

           If (adapter IsNot Nothing) Then
               adapter.Dispose()
           End If

           If (factory IsNot Nothing) Then
               factory.Dispose()
           End If

           Return capture

       End Function

   End Class

End Namespace


Ejemplo de uso:
Código (vbnet) [Seleccionar]
Test.SharpDXUtil.TakeScreenshot().Save("C:\Screenshot.png")

PD: Creo no hace falta decirlo, pero por si acaso: Necesitas descargar la librería de SharpDX y para usar el código de arriba debes referenciar los siguientes ensamblados: SharpDX.dll, SharpDX.Direct3D11.dll, SharpDX.DXGI.dll y SharpDX.Mathematics.dll

Saludos








TrashAmbishion

Disculpa la demora es que tuve problemas con el internet.

En cuanto llegue a casa lo pruebo.

Salu2 y millon de gracias.

TrashAmbishion

Hola,

El código funciona genial mientras no me meta en un juego  :xD :xD

Me da un "access denaid" investigaré a ver que puede estar sucediendo.

Aqui te pongo una captura del error y te pongo la linea en la que ocurre.

Código (vbnet) [Seleccionar]
duplicatedOutput = output1.DuplicateOutput(device)



Eleкtro

Yo ahí no puedo intervenir, todo eso es controlado por SharpDX y es un problema específico (ya que a mi no me ocurre). De todas formas deduzco que ese código genérico HRESULT está indicando un acceso denegado al intentar leer o escribir en la memoria. Publica el problema en la sección de problemas de SharpDX en GitHub: https://github.com/sharpdx/sharpdx/issues

Saludos!








TrashAmbishion

Publicado, ahora resta esperar, mientras sigo con los remiendos usando FRAPS y simulando la tecla de F10. !

:xD :xD :xD

No encuentro entre los ISSUES de alla nada relacionado con esto, me falto probar con las otras DLL's que viene en el mismo paquete quien sabe y si rula !

Salu2 y gracias !