diff --git a/OpenRA.Game/Game.cs b/OpenRA.Game/Game.cs index c3ea6a9c4a..03de13ec0b 100644 --- a/OpenRA.Game/Game.cs +++ b/OpenRA.Game/Game.cs @@ -550,7 +550,7 @@ namespace OpenRA var path = Path.Combine(directory, string.Concat(filename, ".png")); Log.Write("debug", "Taking screenshot " + path); - Renderer.Context.SaveScreenshot(path); + Renderer.SaveScreenshot(path); Debug("Saved screenshot " + filename); } } diff --git a/OpenRA.Game/Graphics/PlatformInterfaces.cs b/OpenRA.Game/Graphics/PlatformInterfaces.cs index 3bf522d431..a1e941f678 100644 --- a/OpenRA.Game/Graphics/PlatformInterfaces.cs +++ b/OpenRA.Game/Graphics/PlatformInterfaces.cs @@ -66,7 +66,6 @@ namespace OpenRA IShader CreateShader(string name); void EnableScissor(int x, int y, int width, int height); void DisableScissor(); - void SaveScreenshot(string path); void Present(); void DrawPrimitives(PrimitiveType pt, int firstVertex, int numVertices); void Clear(); diff --git a/OpenRA.Game/Renderer.cs b/OpenRA.Game/Renderer.cs index e0b7b616bd..fcaded25d3 100644 --- a/OpenRA.Game/Renderer.cs +++ b/OpenRA.Game/Renderer.cs @@ -12,6 +12,8 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading; +using OpenRA.FileFormats; using OpenRA.Graphics; using OpenRA.Primitives; using OpenRA.Support; @@ -308,6 +310,34 @@ namespace OpenRA Window.ReleaseWindowMouseFocus(); } + public void SaveScreenshot(string path) + { + // Pull the data from the Texture directly to prevent the sheet from buffering it + var src = screenBuffer.Texture.GetData(); + var srcWidth = screenSprite.Sheet.Size.Width; + var destWidth = screenSprite.Bounds.Width; + var destHeight = -screenSprite.Bounds.Height; + var channelOrder = new[] { 2, 1, 0, 3 }; + + ThreadPool.QueueUserWorkItem(_ => + { + // Convert BGRA to RGBA + var dest = new byte[4 * destWidth * destHeight]; + for (var y = 0; y < destHeight; y++) + { + for (var x = 0; x < destWidth; x++) + { + var destOffset = 4 * (y * destWidth + x); + var srcOffset = 4 * (y * srcWidth + x); + for (var i = 0; i < 4; i++) + dest[destOffset + i] = src[srcOffset + channelOrder[i]]; + } + } + + new Png(dest, destWidth, destHeight).Save(path); + }); + } + public void Dispose() { WorldModelRenderer.Dispose(); diff --git a/OpenRA.Platforms.Default/OpenGL.cs b/OpenRA.Platforms.Default/OpenGL.cs index 1701b67aa5..a2f351fd6e 100644 --- a/OpenRA.Platforms.Default/OpenGL.cs +++ b/OpenRA.Platforms.Default/OpenGL.cs @@ -287,15 +287,6 @@ namespace OpenRA.Platforms.Default public delegate void Scissor(int x, int y, int width, int height); public static Scissor glScissor { get; private set; } - public delegate void PushClientAttrib(int mask); - public static PushClientAttrib glPushClientAttrib { get; private set; } - - public delegate void PopClientAttrib(); - public static PopClientAttrib glPopClientAttrib { get; private set; } - - public delegate void PixelStoref(int param, float pname); - public static PixelStoref glPixelStoref { get; private set; } - public delegate void ReadPixels(int x, int y, int width, int height, int format, int type, IntPtr data); public static ReadPixels glReadPixels { get; private set; } @@ -429,9 +420,6 @@ namespace OpenRA.Platforms.Default glBlendFunc = Bind("glBlendFunc"); glDepthFunc = Bind("glDepthFunc"); glScissor = Bind("glScissor"); - glPushClientAttrib = Bind("glPushClientAttrib"); - glPopClientAttrib = Bind("glPopClientAttrib"); - glPixelStoref = Bind("glPixelStoref"); glReadPixels = Bind("glReadPixels"); glGenTextures = Bind("glGenTextures"); glDeleteTextures = Bind("glDeleteTextures"); diff --git a/OpenRA.Platforms.Default/Sdl2GraphicsContext.cs b/OpenRA.Platforms.Default/Sdl2GraphicsContext.cs index 570592c9d7..5c62bcca96 100644 --- a/OpenRA.Platforms.Default/Sdl2GraphicsContext.cs +++ b/OpenRA.Platforms.Default/Sdl2GraphicsContext.cs @@ -10,8 +10,6 @@ #endregion using System; -using System.Threading; -using OpenRA.FileFormats; using OpenRA.Graphics; using OpenRA.Primitives; using SDL2; @@ -118,51 +116,6 @@ namespace OpenRA.Platforms.Default OpenGL.CheckGLError(); } - public void SaveScreenshot(string path) - { - var s = window.SurfaceSize; - var raw = new byte[s.Width * s.Height * 4]; - - OpenGL.glPushClientAttrib(OpenGL.GL_CLIENT_PIXEL_STORE_BIT); - - OpenGL.glPixelStoref(OpenGL.GL_PACK_ROW_LENGTH, s.Width); - OpenGL.glPixelStoref(OpenGL.GL_PACK_ALIGNMENT, 1); - - unsafe - { - fixed (byte* pRaw = raw) - OpenGL.glReadPixels(0, 0, s.Width, s.Height, - OpenGL.GL_BGRA, OpenGL.GL_UNSIGNED_BYTE, (IntPtr)pRaw); - } - - OpenGL.glFinish(); - OpenGL.glPopClientAttrib(); - - ThreadPool.QueueUserWorkItem(_ => - { - // Convert GL pixel data into format expected by png - // - Flip vertically - // - BGRA to RGBA - // - Force A to 255 (no transparent pixels!) - var data = new byte[raw.Length]; - for (var y = 0; y < s.Height; y++) - { - for (var x = 0; x < s.Width; x++) - { - var iData = 4 * (y * s.Width + x); - var iRaw = 4 * ((s.Height - y - 1) * s.Width + x); - data[iData] = raw[iRaw + 2]; - data[iData + 1] = raw[iRaw + 1]; - data[iData + 2] = raw[iRaw + 0]; - data[iData + 3] = byte.MaxValue; - } - } - - var screenshot = new Png(data, window.SurfaceSize.Width, window.SurfaceSize.Height); - screenshot.Save(path); - }); - } - public void Present() { VerifyThreadAffinity(); diff --git a/OpenRA.Platforms.Default/ThreadedGraphicsContext.cs b/OpenRA.Platforms.Default/ThreadedGraphicsContext.cs index d5309dd4e8..f4fae71f93 100644 --- a/OpenRA.Platforms.Default/ThreadedGraphicsContext.cs +++ b/OpenRA.Platforms.Default/ThreadedGraphicsContext.cs @@ -49,7 +49,6 @@ namespace OpenRA.Platforms.Default Action doDrawPrimitives; Action doEnableScissor; Action doSetBlendMode; - Action doSaveScreenshot; public ThreadedGraphicsContext(Sdl2GraphicsContext context, int batchSize) { @@ -108,7 +107,6 @@ namespace OpenRA.Platforms.Default context.EnableScissor(t.Item1, t.Item2, t.Item3, t.Item4); }; doSetBlendMode = mode => { context.SetBlendMode((BlendMode)mode); }; - doSaveScreenshot = path => context.SaveScreenshot((string)path); Monitor.Pulse(syncObject); } @@ -447,11 +445,6 @@ namespace OpenRA.Platforms.Default { Post(doSetBlendMode, mode); } - - public void SaveScreenshot(string path) - { - Post(doSaveScreenshot, path); - } } class ThreadedFrameBuffer : IFrameBuffer