Save screenshots via the frame buffer.
This commit is contained in:
@@ -550,7 +550,7 @@ namespace OpenRA
|
|||||||
var path = Path.Combine(directory, string.Concat(filename, ".png"));
|
var path = Path.Combine(directory, string.Concat(filename, ".png"));
|
||||||
Log.Write("debug", "Taking screenshot " + path);
|
Log.Write("debug", "Taking screenshot " + path);
|
||||||
|
|
||||||
Renderer.Context.SaveScreenshot(path);
|
Renderer.SaveScreenshot(path);
|
||||||
Debug("Saved screenshot " + filename);
|
Debug("Saved screenshot " + filename);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,7 +66,6 @@ namespace OpenRA
|
|||||||
IShader CreateShader(string name);
|
IShader CreateShader(string name);
|
||||||
void EnableScissor(int x, int y, int width, int height);
|
void EnableScissor(int x, int y, int width, int height);
|
||||||
void DisableScissor();
|
void DisableScissor();
|
||||||
void SaveScreenshot(string path);
|
|
||||||
void Present();
|
void Present();
|
||||||
void DrawPrimitives(PrimitiveType pt, int firstVertex, int numVertices);
|
void DrawPrimitives(PrimitiveType pt, int firstVertex, int numVertices);
|
||||||
void Clear();
|
void Clear();
|
||||||
|
|||||||
@@ -12,6 +12,8 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using OpenRA.FileFormats;
|
||||||
using OpenRA.Graphics;
|
using OpenRA.Graphics;
|
||||||
using OpenRA.Primitives;
|
using OpenRA.Primitives;
|
||||||
using OpenRA.Support;
|
using OpenRA.Support;
|
||||||
@@ -308,6 +310,34 @@ namespace OpenRA
|
|||||||
Window.ReleaseWindowMouseFocus();
|
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()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
WorldModelRenderer.Dispose();
|
WorldModelRenderer.Dispose();
|
||||||
|
|||||||
@@ -287,15 +287,6 @@ namespace OpenRA.Platforms.Default
|
|||||||
public delegate void Scissor(int x, int y, int width, int height);
|
public delegate void Scissor(int x, int y, int width, int height);
|
||||||
public static Scissor glScissor { get; private set; }
|
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,
|
public delegate void ReadPixels(int x, int y, int width, int height,
|
||||||
int format, int type, IntPtr data);
|
int format, int type, IntPtr data);
|
||||||
public static ReadPixels glReadPixels { get; private set; }
|
public static ReadPixels glReadPixels { get; private set; }
|
||||||
@@ -429,9 +420,6 @@ namespace OpenRA.Platforms.Default
|
|||||||
glBlendFunc = Bind<BlendFunc>("glBlendFunc");
|
glBlendFunc = Bind<BlendFunc>("glBlendFunc");
|
||||||
glDepthFunc = Bind<DepthFunc>("glDepthFunc");
|
glDepthFunc = Bind<DepthFunc>("glDepthFunc");
|
||||||
glScissor = Bind<Scissor>("glScissor");
|
glScissor = Bind<Scissor>("glScissor");
|
||||||
glPushClientAttrib = Bind<PushClientAttrib>("glPushClientAttrib");
|
|
||||||
glPopClientAttrib = Bind<PopClientAttrib>("glPopClientAttrib");
|
|
||||||
glPixelStoref = Bind<PixelStoref>("glPixelStoref");
|
|
||||||
glReadPixels = Bind<ReadPixels>("glReadPixels");
|
glReadPixels = Bind<ReadPixels>("glReadPixels");
|
||||||
glGenTextures = Bind<GenTextures>("glGenTextures");
|
glGenTextures = Bind<GenTextures>("glGenTextures");
|
||||||
glDeleteTextures = Bind<DeleteTextures>("glDeleteTextures");
|
glDeleteTextures = Bind<DeleteTextures>("glDeleteTextures");
|
||||||
|
|||||||
@@ -10,8 +10,6 @@
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Threading;
|
|
||||||
using OpenRA.FileFormats;
|
|
||||||
using OpenRA.Graphics;
|
using OpenRA.Graphics;
|
||||||
using OpenRA.Primitives;
|
using OpenRA.Primitives;
|
||||||
using SDL2;
|
using SDL2;
|
||||||
@@ -118,51 +116,6 @@ namespace OpenRA.Platforms.Default
|
|||||||
OpenGL.CheckGLError();
|
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()
|
public void Present()
|
||||||
{
|
{
|
||||||
VerifyThreadAffinity();
|
VerifyThreadAffinity();
|
||||||
|
|||||||
@@ -49,7 +49,6 @@ namespace OpenRA.Platforms.Default
|
|||||||
Action<object> doDrawPrimitives;
|
Action<object> doDrawPrimitives;
|
||||||
Action<object> doEnableScissor;
|
Action<object> doEnableScissor;
|
||||||
Action<object> doSetBlendMode;
|
Action<object> doSetBlendMode;
|
||||||
Action<object> doSaveScreenshot;
|
|
||||||
|
|
||||||
public ThreadedGraphicsContext(Sdl2GraphicsContext context, int batchSize)
|
public ThreadedGraphicsContext(Sdl2GraphicsContext context, int batchSize)
|
||||||
{
|
{
|
||||||
@@ -108,7 +107,6 @@ namespace OpenRA.Platforms.Default
|
|||||||
context.EnableScissor(t.Item1, t.Item2, t.Item3, t.Item4);
|
context.EnableScissor(t.Item1, t.Item2, t.Item3, t.Item4);
|
||||||
};
|
};
|
||||||
doSetBlendMode = mode => { context.SetBlendMode((BlendMode)mode); };
|
doSetBlendMode = mode => { context.SetBlendMode((BlendMode)mode); };
|
||||||
doSaveScreenshot = path => context.SaveScreenshot((string)path);
|
|
||||||
|
|
||||||
Monitor.Pulse(syncObject);
|
Monitor.Pulse(syncObject);
|
||||||
}
|
}
|
||||||
@@ -447,11 +445,6 @@ namespace OpenRA.Platforms.Default
|
|||||||
{
|
{
|
||||||
Post(doSetBlendMode, mode);
|
Post(doSetBlendMode, mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SaveScreenshot(string path)
|
|
||||||
{
|
|
||||||
Post(doSaveScreenshot, path);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class ThreadedFrameBuffer : IFrameBuffer
|
class ThreadedFrameBuffer : IFrameBuffer
|
||||||
|
|||||||
Reference in New Issue
Block a user