Skip to content

Commit

Permalink
Save screenshots via the frame buffer.
Browse files Browse the repository at this point in the history
  • Loading branch information
pchote committed Aug 27, 2019
1 parent e1fd9cc commit 14caf0f
Show file tree
Hide file tree
Showing 6 changed files with 30 additions and 68 deletions.
2 changes: 1 addition & 1 deletion OpenRA.Game/Game.cs
Expand Up @@ -550,7 +550,7 @@ static void TakeScreenshotInner()
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);
}
}
Expand Down
1 change: 0 additions & 1 deletion OpenRA.Game/Graphics/PlatformInterfaces.cs
Expand Up @@ -63,7 +63,6 @@ public interface IGraphicsContext : IDisposable
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();
Expand Down
29 changes: 29 additions & 0 deletions OpenRA.Game/Renderer.cs
Expand Up @@ -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;
Expand Down Expand Up @@ -300,6 +302,33 @@ public void ReleaseWindowMouseFocus()
Window.ReleaseWindowMouseFocus();
}

public void SaveScreenshot(string path)
{
var src = screenSprite.Sheet.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();
Expand Down
12 changes: 0 additions & 12 deletions OpenRA.Platforms.Default/OpenGL.cs
Expand Up @@ -287,15 +287,6 @@ public static string glGetString(int name)
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; }
Expand Down Expand Up @@ -429,9 +420,6 @@ public static void Initialize()
glBlendFunc = Bind<BlendFunc>("glBlendFunc");
glDepthFunc = Bind<DepthFunc>("glDepthFunc");
glScissor = Bind<Scissor>("glScissor");
glPushClientAttrib = Bind<PushClientAttrib>("glPushClientAttrib");
glPopClientAttrib = Bind<PopClientAttrib>("glPopClientAttrib");
glPixelStoref = Bind<PixelStoref>("glPixelStoref");
glReadPixels = Bind<ReadPixels>("glReadPixels");
glGenTextures = Bind<GenTextures>("glGenTextures");
glDeleteTextures = Bind<DeleteTextures>("glDeleteTextures");
Expand Down
47 changes: 0 additions & 47 deletions OpenRA.Platforms.Default/Sdl2GraphicsContext.cs
Expand Up @@ -10,8 +10,6 @@
#endregion

using System;
using System.Threading;
using OpenRA.FileFormats;
using OpenRA.Graphics;
using OpenRA.Primitives;
using SDL2;
Expand Down Expand Up @@ -112,51 +110,6 @@ public void DisableScissor()
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();
Expand Down
7 changes: 0 additions & 7 deletions OpenRA.Platforms.Default/ThreadedGraphicsContext.cs
Expand Up @@ -49,7 +49,6 @@ sealed class ThreadedGraphicsContext : IGraphicsContext
Action<object> doDrawPrimitives;
Action<object> doEnableScissor;
Action<object> doSetBlendMode;
Action<object> doSaveScreenshot;

public ThreadedGraphicsContext(Sdl2GraphicsContext context, int batchSize)
{
Expand Down Expand Up @@ -102,7 +101,6 @@ void RenderThread(object contextObject)
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);
}
Expand Down Expand Up @@ -436,11 +434,6 @@ public void SetBlendMode(BlendMode mode)
{
Post(doSetBlendMode, mode);
}

public void SaveScreenshot(string path)
{
Post(doSaveScreenshot, path);
}
}

class ThreadedFrameBuffer : IFrameBuffer
Expand Down

0 comments on commit 14caf0f

Please sign in to comment.