Rewrite screenshot saving.

This commit is contained in:
Paul Chote
2019-02-24 12:45:48 +00:00
committed by reaperrr
parent 5f212a99fe
commit 5a1124426d
5 changed files with 46 additions and 47 deletions

View File

@@ -530,31 +530,19 @@ namespace OpenRA
static void TakeScreenshotInner() static void TakeScreenshotInner()
{ {
Log.Write("debug", "Taking screenshot"); using (new PerfTimer("Renderer.SaveScreenshot"))
Bitmap bitmap;
using (new PerfTimer("Renderer.TakeScreenshot"))
bitmap = Renderer.Context.TakeScreenshot();
ThreadPool.QueueUserWorkItem(_ =>
{ {
var mod = ModData.Manifest.Metadata; var mod = ModData.Manifest.Metadata;
var directory = Platform.ResolvePath(Platform.SupportDirPrefix, "Screenshots", ModData.Manifest.Id, mod.Version); var directory = Platform.ResolvePath(Platform.SupportDirPrefix, "Screenshots", ModData.Manifest.Id, mod.Version);
Directory.CreateDirectory(directory); Directory.CreateDirectory(directory);
var filename = TimestampedFilename(true); var filename = TimestampedFilename(true);
var format = Settings.Graphics.ScreenshotFormat; var path = Path.Combine(directory, string.Concat(filename, ".png"));
var extension = ImageCodecInfo.GetImageEncoders().FirstOrDefault(x => x.FormatID == format.Guid) Log.Write("debug", "Taking screenshot " + path);
.FilenameExtension.Split(';').First().ToLowerInvariant().Substring(1);
var destination = Path.Combine(directory, string.Concat(filename, extension));
using (new PerfTimer("Save Screenshot ({0})".F(format))) Renderer.Context.SaveScreenshot(path);
bitmap.Save(destination, format); Debug("Saved screenshot " + filename);
}
bitmap.Dispose();
RunAfterTick(() => Debug("Saved screenshot " + filename));
});
} }
static void InnerLogicTick(OrderManager orderManager) static void InnerLogicTick(OrderManager orderManager)

View File

@@ -61,7 +61,7 @@ namespace OpenRA
IShader CreateShader(string name); IShader CreateShader(string name);
void EnableScissor(int left, int top, int width, int height); void EnableScissor(int left, int top, int width, int height);
void DisableScissor(); void DisableScissor();
Bitmap TakeScreenshot(); 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();

View File

@@ -11,7 +11,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Drawing.Imaging;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using OpenRA.Graphics; using OpenRA.Graphics;
@@ -166,8 +165,6 @@ namespace OpenRA
public string Language = "english"; public string Language = "english";
public string DefaultLanguage = "english"; public string DefaultLanguage = "english";
public ImageFormat ScreenshotFormat = ImageFormat.Png;
} }
public class SoundSettings public class SoundSettings

View File

@@ -11,7 +11,11 @@
using System; using System;
using System.Drawing; using System.Drawing;
using System.IO;
using System.Threading;
using OpenRA.FileFormats;
using OpenRA.Graphics; using OpenRA.Graphics;
using OpenRA.Support;
using SDL2; using SDL2;
namespace OpenRA.Platforms.Default namespace OpenRA.Platforms.Default
@@ -111,39 +115,49 @@ namespace OpenRA.Platforms.Default
OpenGL.CheckGLError(); OpenGL.CheckGLError();
} }
public Bitmap TakeScreenshot() public void SaveScreenshot(string path)
{ {
var rect = new Rectangle(Point.Empty, window.SurfaceSize); var s = window.SurfaceSize;
var bitmap = new Bitmap(rect.Width, rect.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb); var raw = new byte[s.Width * s.Height * 4];
var data = bitmap.LockBits(rect,
System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
OpenGL.glPushClientAttrib(OpenGL.GL_CLIENT_PIXEL_STORE_BIT); OpenGL.glPushClientAttrib(OpenGL.GL_CLIENT_PIXEL_STORE_BIT);
OpenGL.glPixelStoref(OpenGL.GL_PACK_ROW_LENGTH, data.Stride / 4f); OpenGL.glPixelStoref(OpenGL.GL_PACK_ROW_LENGTH, s.Width);
OpenGL.glPixelStoref(OpenGL.GL_PACK_ALIGNMENT, 1); OpenGL.glPixelStoref(OpenGL.GL_PACK_ALIGNMENT, 1);
OpenGL.glReadPixels(rect.X, rect.Y, rect.Width, rect.Height, OpenGL.GL_BGRA, OpenGL.GL_UNSIGNED_BYTE, data.Scan0);
OpenGL.glFinish();
OpenGL.glPopClientAttrib();
// Reset alpha channel to fully opaque
unsafe unsafe
{ {
var colors = (int*)data.Scan0; fixed (byte* pRaw = raw)
var stride = data.Stride / 4; OpenGL.glReadPixels(0, 0, s.Width, s.Height,
for (var y = 0; y < rect.Height; y++) OpenGL.GL_BGRA, OpenGL.GL_UNSIGNED_BYTE, (IntPtr)pRaw);
for (var x = 0; x < rect.Width; x++)
colors[y * stride + x] |= 0xFF << 24;
} }
bitmap.UnlockBits(data); OpenGL.glFinish();
OpenGL.glPopClientAttrib();
// OpenGL standard defines the origin in the bottom left corner which is why this is upside-down by default. ThreadPool.QueueUserWorkItem(_ =>
bitmap.RotateFlip(RotateFlipType.RotateNoneFlipY); {
// 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;
}
}
return bitmap; var screenshot = new Png(data, window.SurfaceSize.Width, window.SurfaceSize.Height);
screenshot.Save(path);
});
} }
public void Present() public void Present()

View File

@@ -43,13 +43,13 @@ namespace OpenRA.Platforms.Default
Action doPresent; Action doPresent;
Func<string> getGLVersion; Func<string> getGLVersion;
Func<ITexture> getCreateTexture; Func<ITexture> getCreateTexture;
Func<Bitmap> getTakeScreenshot;
Func<object, IFrameBuffer> getCreateFrameBuffer; Func<object, IFrameBuffer> getCreateFrameBuffer;
Func<object, IShader> getCreateShader; Func<object, IShader> getCreateShader;
Func<object, IVertexBuffer<Vertex>> getCreateVertexBuffer; Func<object, IVertexBuffer<Vertex>> getCreateVertexBuffer;
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)
{ {
@@ -86,7 +86,6 @@ namespace OpenRA.Platforms.Default
doPresent = () => context.Present(); doPresent = () => context.Present();
getGLVersion = () => context.GLVersion; getGLVersion = () => context.GLVersion;
getCreateTexture = () => new ThreadedTexture(this, (ITextureInternal)context.CreateTexture()); getCreateTexture = () => new ThreadedTexture(this, (ITextureInternal)context.CreateTexture());
getTakeScreenshot = () => context.TakeScreenshot();
getCreateFrameBuffer = s => new ThreadedFrameBuffer(this, context.CreateFrameBuffer((Size)s, (ITextureInternal)CreateTexture())); getCreateFrameBuffer = s => new ThreadedFrameBuffer(this, context.CreateFrameBuffer((Size)s, (ITextureInternal)CreateTexture()));
getCreateShader = name => new ThreadedShader(this, context.CreateShader((string)name)); getCreateShader = name => new ThreadedShader(this, context.CreateShader((string)name));
getCreateVertexBuffer = length => new ThreadedVertexBuffer(this, context.CreateVertexBuffer((int)length)); getCreateVertexBuffer = length => new ThreadedVertexBuffer(this, context.CreateVertexBuffer((int)length));
@@ -103,6 +102,7 @@ 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);
} }
@@ -437,9 +437,9 @@ namespace OpenRA.Platforms.Default
Post(doSetBlendMode, mode); Post(doSetBlendMode, mode);
} }
public Bitmap TakeScreenshot() public void SaveScreenshot(string path)
{ {
return Send(getTakeScreenshot); Post(doSaveScreenshot, path);
} }
} }